領先一步
VMware 提供培訓和認證,助您加速進步。
瞭解更多在這篇博文中,我將向您展示如何使用 Spring Integration 和 dm Server 建立一個松耦合且可擴充套件的應用程式。使用 OSGi 的額外好處是,我們可以在執行時更改應用程式的行為,當然我們也會從中獲得一些樂趣。首先,我將快速強調設計應用程式以供併發使用的原因,然後我將描述將 OSGi 捆綁包與訊息傳遞整合的不同策略。在此過程中,您將一瞥我們的工具和 dm Server 的一些功能。如果您已下載並安裝最新的 SpringSource Tool Suite 和 dm Server,您應該能夠自己完成此操作。您不需要示例程式碼來跟隨故事,但如果您有興趣,它將在 Spring Integration 沙盒中提供。
同步崩潰
應用程式中的同步呼叫執行良好,但它們無法擴充套件。之前已經有很多文件記載,所以我將快速回顧一下然後繼續。當您進行同步呼叫時,呼叫執行緒必須阻塞直到呼叫完成。如果您在該方法中進行 I/O,阻塞執行緒會浪費 CPU 週期。如果您有一個大規模並行 Web 應用程式,這不是一個大問題,因為您只需增加您正在使用的執行緒池的最大大小。但是,如果您需要從單個呼叫中充分利用伺服器的 CPU 算力,那您就不走運了。由於一個執行緒執行在一個核心上,您擁有的核心越多,單執行緒程式的效率就越低。
訊息傳遞救援
自己進行併發程式設計很有趣,但它不是最容易做的事情,您可能想今天早點下班。這就是 Spring Integration 可以幫助您的地方。它為您提供了您所需的所有併發處理控制,而無需您承擔併發程式設計的低階細節。
OSGi 如何提供幫助?
您可以完全防止沒有 OSGi 的同步崩潰。它只是讓生活變得更好,您不應該錯過它。每次進行一點更改時都進行完全重新部署和重新啟動 JVM 將不會很有趣。還有一件事使 OSGi 和訊息傳遞的組合非常有趣。如果企業應用程式變得龐大,您遲早需要進行模組化。在同一個傳統伺服器中執行多個模組是災難的根源(也稱為 jar 地獄)。因此,在許多情況下,在傳統架構中,人們會過早地進行擴充套件。這會造成非常糟糕的設定。不同的節點必須透過網路相互通訊,並且它們不能共享 CPU 和記憶體等資源。這使得架構效率低下並嚴重影響效能。您不僅在一個伺服器的某些核心上浪費週期,現在您還在多個節點上重複同樣的效率低下。更糟糕的是,網路延遲增加了 I/O 等待,而正常的本地方法呼叫就足夠了。
使用 OSGi,您不必承擔這種損失,您可以在同一個 JVM 中執行不同的捆綁包,這樣它們之間的呼叫就像正常的方***一樣快,而沒有與在同一個類路徑上部署不同團隊的 jar 相關聯的可怕的 jar 地獄。擁有一個可以在執行時更改其行為、由多個團隊協作開發並幫助您將所需硬體減少到單個強大伺服器的應用程式。聽起來很棒,那麼我們開始吧。
如果您喜歡使用實際程式碼示例或自己並行編寫示例來跟隨部落格,則需要準備好適當的工具(和程式碼)。要跟隨部落格的思路,您不需要這樣做。我將在此處包含必要的程式碼示例。本文無意成為詳細的操作指南,因此如果您想跟隨,您將不得不自己編寫程式碼,或者只是使用示例。為此,請按照以下步驟操作。
在本節中,我們將使用一個依賴於 Spring Integration 的單個捆綁包,並將我們的工作移交給它。這個捆綁包將隱藏所有訊息傳遞細節,如果您系統只有一小部分需要做密集工作,這可能正是您所需要的。
我將展示的示例應用程式是為一座中世紀城鎮開發的。在這些城鎮中,過去有一位城市傳令官會在街上高聲宣告當地新聞和公告。這個城鎮(不願透露姓名)已被城市傳令官工會強迫改善其城市傳令官工作人員的工作條件,並且由於預算限制,他們希望同時減少人員。
他們得出結論,實現這些目標的唯一方法是用自動化系統取代城市傳令官的大部分體力勞動。城市傳令官只需在舒適的辦公室裡將他的訊息傳遞給這個系統,然後訊息就會分發給鎮上的所有公民。市政廳已經跟蹤所有公民,所以將這個分發邏輯放在城鎮的市政廳最容易。
[caption id="attachment_1096" align="alignnone" width="653" caption="示例中各種捆綁包的概述。"]
[/caption]
城市傳令官捆綁包 決定城市傳令官不應重新接受培訓以使用新系統,因此他仍將呼叫城鎮的 cry() 方法,他只是不再需要多次進行才能覆蓋城鎮的每個部分。公告的分發將由城鎮中的新系統透明地完成。towncrier 捆綁包只有一個 TownCrier.java 類,它被注入了它應該向其哭喊的 Town。
@Component
public class TownCrier {
private static final Log logger = LogFactory.getLog(TownCrier.class);
@Autowired //Spring will take care of the wiring as usual
private Town town;
private ScheduledThreadPoolExecutor schedule;
@PostConstruct
public void start() {
logger.info("Starting feeder");
schedule = new ScheduledThreadPoolExecutor(1);
TimerTask task = new TimerTask() {
public void run() {
logger.info("Crying the time");
town.cry("Oyez! Oyez! Oyez! It is now " + new Date());
}
};
schedule.scheduleAtFixedRate(task, 1, 1, TimeUnit.SECONDS);
}
}
如果您瞭解 Spring Integration,您會對這段程式碼感到畏縮,並希望使用入站通道介面卡和輪詢器,但我們只是在這裡模擬遺留程式碼,所以請耐心等待。
為了幫助 Spring 引用將放在另一個捆綁包中的 Town,我們將使用 Spring DM。如果您以前從未接觸過 OSGi 和 Spring DM,現在是閱讀 入門指南 的好時機。我們只需在 osgi-context.xml 中新增一個 OSGi 引用元素。
<osgi:reference id="collectorService"
interface="com.springsource.samples.integration.osgi.town.input.Town" />
最後,MANIFEST.mf 中有一個對匯出服務的 town 捆綁包的依賴項。
Import-Package: com.springsource.samples.integration.osgi.town.input,
org.apache.commons.logging;version="[1.1.1,1.1.1]",
javax.annotation
同樣,完整程式碼在 Spring Integration 沙盒中
城鎮捆綁包 城鎮(捆綁包)已完全採用訊息傳遞。他們正在使用 Spring Integration 來為他們處理訊息傳遞和併發。他們還定義了城市傳令官和公民(稍後提及)所需的輸入和輸出介面。
Town 介面有一個 cry 方法,我們希望向城市傳令官公開它的一個實現。我們可以讓 Spring Integration 為我們建立一個實現,該實現接收字串引數並將其作為訊息包裝在通道上。這是透過閘道器完成的。
<gateway id="inputGateway" default-request-channel="announcements"
service-interface="com.springsource.samples.integration.osgi.town.input.Town" />
<publish-subscribe-channel id="announcements" />
public interface Town {
void cry(String string);
}
由於 Town 介面只有一個方法,我們不需要告訴 Spring Integration 呼叫哪個方法,所以這裡的 @Gateway 是可選的。
執行時監聽
鎮上的居民將在市政廳登記,但不知道訊息傳遞。公民可以透過其 onCry(String string) 方法監聽哭聲,但對 Spring Integration 沒有依賴。在某些情況下,您可能希望執行一個不依賴 Spring Integration 的捆綁包,但在事件驅動架構中使用 Spring Integration 來啟用它。
該示例在 CityHall 的 Town 捆綁包中使用了一個服務啟用器,負責解包訊息並將它們推送到公民。
@ServiceActivator
public void onCry(String announcement) {
for (Citizen consumer : citizens) {
consumer.onCry(announcement);
}
}
為了允許公民自行註冊,CitizenRegistry 作為 OSGi 服務公開。
<osgi:service id="citizenRegistry" ref="cityHall"
interface="com.springsource.samples.integration.osgi.town.output.CitizenRegistry" />
公民註冊服務也由市政廳實現,以維護公告分發到的公民集合。
在 scribe 捆綁包中實現了一個 Citizen 示例。它透過 OSGi 引用引用 CitizenRegistry。
<osgi:reference id="citizenRegistry"
interface="com.springsource.samples.integration.osgi.town.output.CitizenRegistry" />
抄寫員在初始化期間向公民登錄檔註冊自己,然後記錄它收到的所有公告。
這樣,只有城鎮捆綁包的程式碼需要更改才能開始使用新系統,而公民捆綁包可以保持不變。當然,可能有充分的理由讓多個捆綁包瞭解正在進行的訊息傳遞並直接共享通道。在下一節中,我們將深入探討這一點。
假設城鎮也與中央政府打交道。政府以政府捆綁包的形式派代表到城鎮。該代表直接瞭解公告通道,因此無需使用閘道器或服務啟用器來遮蔽它。城鎮將 announcementsChannel 公開為 OSGi 服務。
<osgi:service id="announcementsChannel" ref="announcements"
interface="org.springframework.integration.channel.SubscribableChannel" />
為了將政府法令傳送到城鎮的公告通道,我們在示例中的政府捆綁包中有一個閘道器,但這可以是任何端點。重要的是要在政府捆綁包中引用城鎮捆綁包中的通道。
<osgi:reference id="announcementsChannel"
interface="org.springframework.integration.channel.SubscribableChannel" />
現在您可以在另一個捆綁包中將其用作普通通道
<si:service-activator input-channel="announcementsChannel"
ref="nationalArchive" />
<si:gateway
service-interface="com.springsource.samples.integration.osgi.government.GovernmentDecreeGateway"
id="decreeer" default-request-channel="announcementsChannel" />
為了簡單起見,該示例實現了非常簡單的本地端點,但是一旦通道橋接了捆綁包,就沒有任何東西可以阻止您使用 Spring Integration 的介面卡離開 JVM。
假設政府捆綁包成為銀行的內部貸款/信貸部門(其對其他方的貸款利率可能會根據聯邦利率而變化——可以建立在不同的架構上)。我不是金融專家,也不是您領域的專家,所以我不會妄加揣測這個技術解決方案究竟如何應用。未來,我們將把示例重構為一個更真實的領域,並將其新增到示例中,為您提供一些想法。如果您有特殊要求,請務必在此處評論。
要設計一個可擴充套件的應用程式,您需要考慮同步移交的問題。訊息傳遞可以解決許多這些問題。對於為了最佳效能最好在單個 JVM 上執行的大型應用程式的發展,OSGi 是一個引人注目的替代方案。將 Spring Integration 和 dm Server 混合使用是強大而簡單的。在本文中,您已經瞭解瞭如何