Shuttle(飛梭)服務總線是一個免費的.NET開源軟件項目,它為開發面向消息的事件驅動架構(EDA)系統提供了一種新方法。盡管它仍處於起步階段,不過它已被應用於生產系統之中。
相關要點如下:
- 用C#(基於.NET 3.5)開發而成
- 核心功能不依賴於任何第三方產品或項目
- 既支持命令消息,又支持事件消息(Pub/Sub)
- 具有集成的消息分發功能
- 包含一個命令行管理程序,以便輕松應對各種操作要求
- 廣泛使用接口,以便替換或擴展功能
- 通過自動重試提供容錯能力
為何要使用服務總線?
盡管在使用服務總線(service bus)時需要做一些思維模式的轉變,不過這么做卻是大有裨益的。通過在你的系統中設計一條服務總線,讓其在特定終結點(endpoint)上執行非常明確的功能,這樣你就可以專心設計在隔離環境下運行的那一小部分軟件。可以對此類終結點進行獨立地版本控制及維護,而前提是你對此類終結點所做的解耦工作已達到所需的程度。
基本上是說,你最終會在不同組件之間發送消息,並對這些消息進行異步處理。由於這會導致出現“即發即棄(fire-and-forget)”的情況,因此需要認真考慮系統的工作方式。而由此帶來的用戶體驗可能完全不同於傳統實現,即那種立即處理對用戶所請求的全部操作的方式。
比如就是簡單地發送一封電子郵件。你可以先設置好某個終結點去處理與之有關的命令消息類型(command message type)。然后,當你需要發送電子郵件時,你就可以使用如下代碼:
Bus.Send(new SendEMailCommand { From = "someone@from.com", To = "someone@to.com", Subject = "testing e-mail", Body = "Hello" });
你可能想知道,這么做與你自己直接發送此郵件的到底有何不同之處:
- 這條消息發送調用是即時的,而我們不用等着此調用執行完成
- 由於請求會被排入隊列,因此就避免了瓶頸
- 要是此郵件發送失敗,就會自動重發此郵件
- 只要得到的數據正確無誤,就可以保證此郵件最終會被發出去;或者,至少在發生問題時可以采取手動操作進行處理
因此,客戶端代碼不用關心該終結點到底是如何發送數據的。而終結點可能會用簡單郵件傳輸協議(SMTP)發送數據,甚至還可能會用某種自定義的網絡服務(web-service)發送數據。
Shuttle是如何發揮其魔力的?
Shuttle服務總線依賴於兩件事:
- 消息(Message),及
- 隊列(Queue)
隊列基礎設施可以是任何實現。通常情況下,你會想使用某種真正的隊列技術,例如微軟消息隊列(MSMQ)。目前Shuttle提供直接現成支持的隊列有,微軟消息隊列(MSMQ)及Sql Server基於表的隊列(table-based queues)。如果你想使用任何其他實現,要解決的問題就是實現有關接口,因此你會干得不錯。Shuttle使用統一資源標識符(URI)的結構去表示隊列,例如:
- msmq://{machine}/{queue}
- sql://{connection-name}/{table}
為了實現你自己的隊列,你只需簡單挑選一種方案,並在你的隊列實現中解析這種結構。
為了組織你的終結點,Shuttle使用了通用服務主機去簡化部署。讓一個新的終結點生效,就是解決該終結點所用隊列的配置問題,然后啟動你的服務即可,正如以下配置文件及代碼片段所示:
<?xml version="1.0"?> <configuration> <configSections> <section name="serviceBus" type="Shuttle.ESB.Core.ServiceBusSection, Shuttle.ESB.Core"/> </configSections> <serviceBus> <inbox workQueueUri="msmq://./inbox-work" journalQueueUri="msmq://./inbox-journal" errorQueueUri="msmq://./shuttle-error" /> </serviceBus> </configuration> public class ServiceBusHost : IHost, IDisposable { private IServiceBus bus; public void Dispose() { bus.Dispose(); } public void Start() { bus = ServiceBus .Default() .Start(); } }
應該注意的是,通用主機既能以控制台應用程序的方式運行,又能作為服務去安裝。這樣就讓調試終結點變得特別容易,因為當你在Visual Studio中調試時,你可以將通用主機指定為啟動應用程序。
一旦通用主機從收件箱隊列中獲取到一條特定的消息,通用主機就會嘗試找到處理程序,並將消息傳送給此處理程序以供處理。至於那些找不到處理程序的消息,或被移至錯誤隊列,或被丟棄,采取何種處理方式就要根據配置文件中所指定的信息而定了。
命令消息與事件消息
由於命令(command)是一個要求執行特定功能的明確請求,因此命令會被發送至單獨的終結點。這意味着,為了發送消息,你需要知道某個終結點實現了特定的行為。因此,命令導致了更高程度的行為耦合。而如果在發送消息時,那個接收消息的終結點是可配置的,那么便可隨時更改終結點。
譬如,你可能有如下命令:
- SendEMailCommand
- ConvertDocumentCommand
- DeleteFileCommand
- CancelOrderCommand
剛好相反,事件(event)可能沒有或有多個訂閱者(subscriber)。通常情況下,事件應該被明確定義,因為從業務角度看必需這么做。因此,按理說,除非是那種根據未來需求定義的事件,否則事件至少會有一個訂閱者。一旦事件被發布出來,每個訂閱者都會收到一份事件消息副本。
這不同於消息分發(message distribution),因為一條分布式消息只會被發送至一個工作者的收件箱隊列。
譬如,你可能有如下事件:
- EMailSentEvent
- DocumentConvertedEvent
- FileDeletedEvent
- OrderCancelledEvent
為了在消息中添加任何必需的即席數據(ad-hoc data),Shuttle服務總線允許你為消息指定消息頭。此外,還可以使用相關ID(correlation ID)去對有關消息進行分組。
可伸縮性(Scalability)
Shuttle使你獲得了不少的可伸縮性(scalability),由於消息被排入隊列,因此也就不會出現刻不容緩的緊迫瓶頸。即使有些終結點可能會被配置成多線程的,但是這並不意味着某個特定的終結點不會由於接收過量消息而導致性能下降。每個終結點都要有將消息分發給其他終結點的內置能力,一旦任何其他終結點通知分配器(distributor)它有可執行工作的空閑線程,就可以運用此能力分發消息。而要做到這一點,只需配置一下就好了。
模塊(Modules)
Shuttle使用了一種可觀測的管道(pipeline)結構。各種事件以特定順序被注冊到管道中,而觀察者(observer)也可以被注冊到管道中去響應各自的事件。
為了保持可擴展性(extensibility),你可以把自己的模塊實現注冊到Shuttle之中。通過添加響應各自管道事件的觀察者,這些模塊通常會與特定的管道相連。而且為了隨需應變,你甚至可以把自定義事件添加到管道中。
對於依賴注入及日志記錄的支持怎么樣?
由於Shuttle為了解耦而如此廣泛地使用了接口,因此你就可以根據個人喜好,自由接入任何依賴注入(DI)或日志記錄的實現。默認實現不依賴於任何第三方組件,不過目前還有作為依賴注入實現的Castle Windsor及作為日志記錄實現的Log4Net可供選用。
生產環境下的Shuttle案例
Shuttle在南非某大型短期保險公司的實施已大獲成功,取代了陳舊的文檔索引系統。
客戶通過電子郵件發送與索賠有關的文檔,而郵件會被Lotus Domino電子郵件系統接收到。然后,FileNet Email Manager(電子郵件管理器)應用程序會將這些電子郵件提取出來,並放置到IBM FileNet Content Engine(內容引擎)中。內容引擎經過配置,用以響應任何電子郵件分類為已提交的新文檔。內容引擎被設計用於處理這些送達郵件,並寫出一份包含相關數據的XML文件。
從那里開始,Shuttle Content Engine(內容引擎)終結點會拾取這些XML文件,然后發布事件以表明新內容已被放置到了內容引擎中。由於此終結點的結構並沒有具體到索引過程,因此就有可能重用此終結點,用於發布任何新內容。而且應由此內容引擎負責簡單寫出相關的XML文檔。
Shuttle Indexing(索引)終結點訂閱那些新的內容消息,而且消息一送達,終結點就開始跟蹤與特定郵件一起送達的所有文檔,因為每封郵件都有唯一標示符,而且此標示符已被附加到每份文檔的內容引擎元數據中。一旦搜集到與一封郵件相關的所有文檔,命令消息就會被發送至Shuttle Document Conversion(文檔轉換)終結點,用以將HTML格式郵件正文及全部JPG格式文件轉換為TIFF格式文檔。
文檔轉換終結點對於索引過程一無所知,而僅僅執行各種文檔轉換。一旦某個文檔轉換完成,就會發布事件以通知某個文檔轉換已成功或失敗。任何系統需要文檔轉換的系統都會訂閱這些事件消息。為了既能建立針對於特定系統的消息,也能建立不針對於特定系統的消息,請求轉換的系統可以在轉換請求命令中使用相關ID(correlation ID),和/或將名/值對頭添加到傳出的轉換請求命令消息中。這些頭信息始終會被附加到由Shuttle所發送的任何相關消息中。
一旦完成了所有需要的文檔轉換,就會給Shuttle OvaFlo終結點發送一條命令消息,用以在IBM FileNet Process Engine(流程引擎)中創建索引工作流實例。OvaFlo是由Ovations Group公司開發的一款元工作流(meta-workflow)框架產品。
為了執行實際的索引,用戶會訪問基於網絡的索引應用程序。此應用程序會從OvaFlo終結點中得到下一個可用索引工作流實例,並顯示相關文檔的分類。每份文檔會被鏈接到一個具體的索賠、及輸入的任何其他的相關索引數據。此外,也可以用於處理系統用戶所提出的將某些文檔轉換為TIFF格式的請求。當把各種分門別類的文檔放置到一個文件中時,此功能就特別有用。一旦用戶覺得對數據很滿意,就可以提交該任務以示完成。這一步是通過發出一條命令消息后異步處理的,以便讓用戶可以立即繼續處理下一索引任務。
在某些情況下,會發現來自web前端的工作卻在后台系統工作中排隊。譬如文檔轉換就是這種情況,其中在從前端發起請求之前,來自送達郵件的所有需要的轉換都在被處理中。我們通過通用主機,使用相同的編譯程序集 ,就可以簡單安裝一個單獨的Shuttle Document Conversion(文檔轉換)終結點,不過我們要修改此終結點的配置,以便使用其自身隊列。然后前端將轉換請求發送給這個高優先級終結點,接着那些轉換會得到及時地處理。而所有的后台轉換仍會被發送至原有的終結點。
因此,在這個例子中,Shuttle被用於將不同的系統粘合到一起。由於該系統具有所需的內置容錯能力,因此它比前一系統要穩定得多。而且由於沒有積壓工作被創建出來,因此系統性能非常出色。
結論
Shuttle為你在實現企業服務總線(ESB)時提供了另一種自由選擇。這個項目托管在CodePlex上:
優點:
- 新項目
- 高度可擴展
- 免費開源軟件
- 命令行管理程序
缺點:
- 新項目
- 必須手動處理進程狀態數據
隨着時間的推移和社區的支持,出現了各種不同的實現可用於擴展,其中包括更多的隊列、依賴注入(DI)、及其他選擇。