熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java核心技術 >> 正文

基於WCF和MSMQ構建發布/訂閱消息總線

2013-11-23 19:19:39  來源: Java核心技術 

  本文是翻譯Tom Hollander先生的blog文章《Building a Pub/Sub Message Bus with WCF and MSMQ》對英文blog文章感興趣的朋友可以直接訪問文章下面的鏈接譯者開源論壇小組

  這幾年經常談到采用事件驅動架構(eventdriven architecture)技術來構建可擴展的可維護的系統我發現在一些方案中這是非常有趣的模型但是在Microsoft平台上這種架構一直沒有很好的支持因此許多人發現比較難以實現幾年以前在一個系統中我采用Net Remoting / MSMQ / HTTP 構建了發布/訂閱消息總線(Pub/Sub Message Bus)但是覺得不是很完美很多地方實現比較困難並且需要定制的代碼如承載隊列監聽(host the queue listeners)編碼/解碼消息處理可靠性和管理訂閱者等等

  在我目前的項目中我再一次嘗試了這一模型然而這幾年技術發展有很大的變化我高興的說我的體驗比以前要好多了在說明我的方案之前我想提醒的一點是我僅僅是描述實現這一模型的一種方法在不同的應用需求情況下應該有其他更合適的方法因為我工作的項目是一個大的Net應用系統因此跨平台的互操作不需要考慮(真幸運!)

  我們完成的項目構建在NET Framework 平台並使用了WCF / MSMQ / IIS 部署在Windows server 平台上

  定義Service Contract
    第一步定義Contact發布者用來通知訂閱者發生事件在我們的項目中有許多不同的事件類型但是為了盡可能重用代碼我們使用了泛型Service Contract
    [ServiceContract]
    public interface IEventNotification<TLog>
    {
        [OperationContract(IsOneWay = true)]
        void OnEventOccurred(TLog value);
    }

  對於任一事件類型我們可以簡單定義一個Data Contract 來負載數據(carry the payload)並提供一個繼承的Service Contract 類型如下所示
    [ServiceContract]
    public interface IAccountEventNotification : IEventNotification<AccountEventLog>
    {
    }實現發布者(Publisher)
    發布/訂閱模型中最重要的一點是發布和訂閱方應該是低耦合的尤其是發布者不應該知道訂閱者的任何事情包括多少個訂閱者和訂閱者在哪裡起初我們試圖采用MSMQ的PGM多播特性來實現 – 實質上讓你定義單個隊列地址悄悄地路由相同的消息到多個目標隊列雖然這可以滿足要求但是這一方案也存在一些缺點第一在WCF中使用多播隊列地址的唯一方式是采用MsmqIntegrationBindingMsmqIntegrationBinding沒有NetMsmqBinding靈活其次多播地址僅僅適用非事務隊列(nontransactional queues)這樣對我們的系統產生不能接受的可靠性影響

  因此我們放棄了這一方案決定在發布者實現我們自己的輕量級多播技術上而言這樣打破了發布者完全不了解訂閱者這一黃金規則所有訂閱者的信息完全存放在配置文件中這意味著我們增加修改或刪除訂閱者完全不影響代碼

  我們開發一個組件ServiceFactory(與p&p Web Service Software Factory 沒有關系)ServiceFactory只是一個簡單的抽象通過讀取配置文件來創建本地或WCF實例ServiceFactory組件沒有對外公布但是你可以簡單替換為你喜歡的依賴注入框架(Dependency Injection Framework)達到相同的效果在我們的系統中Web Services的配置文件nfig 可能有如下定義的依賴服務
    <serviceFactory>     <services>         <addname=EmailUtility contract=MyProjectIEmailUtility MyProject type=MyProjectEmailUtility MyProject mode=SameAppDomain instanceMode=Singleton enablePolicyInjection=false />         <addname=SubsctiberXAccountEventNotificationcontract=MyProjectContractsIAccountEventNotification MyProjectContractsmode=Wcfendpoint=SubsctiberXAccountEventNotification />         <addname=SubsctiberYAccountEventNotification contract=MyProjectContractsIAccountEventNotification MyProjectContracts mode=Wcf endpoint=SubsctiberYAccountEventNotification />     </services> </serviceFactory>

  我們使用ServiceFactory創建單個實例代碼如下
    IEmailUtility email = ServiceFactoryGetService<IEmailUtility>();

  根據上面的配置文件上述代碼將獲得一個本地EmailUtility單件實例但是不同的配置可以返回一個WCF proxy 代理類實例可以非常方便重用ServiceFactory組件返回所有配置的匹配特定Contract的服務我們將根據這些構建NotificationPublisher類代碼如下
    public class NotificationPublisher<TInterface TLog>
        where TInterface : class IEventNotification<TLog>
    {
        public static void OnEventOccurred(TLog value)
        {
            List<TInterface> subscribers = ServiceFactoryGetAllServices<TInterface>();

  foreach (TInterface subscriber in subscribers)
            {
                subscriberOnEventOccurred(value);
            }
        }
    }

  根據上述代碼發布者發布事件所需要做的是傳入合適的泛型參數實例化NotificationPublisher對象並調用OnEventOccured 方法假定我們使用IAccountEventNotification 接口和上述配置這樣事件將通過WCF到達SubscriberXAccountEventNotification 和 SubscriberYAccountNotification 端點並觸發相關事件

  配置發布者
    發布端最後一部分是WCF配置如上述所提及的我們選擇使用MSMQ提供可靠的異步的消息傳遞過去編寫MSMQ代碼比較困難但是對WCF編程模型而言MSMQ與其他傳輸協議沒什麼區別在我們的案例中我們選擇了NetMsmqBindingNetMsmqBinding 為核心MSMQ特性提供了全面訪問WCF功能(與MsmqIntegrationBinding不同MsmqIntegrationBinding提供了更豐富的MSMQ支持但是限制了WCF功能)

  如下是客戶端的WCF的配置示例
    <systemserviceModel>     <bindings>         <netMsmqBinding>            <bindingname=TransactionalMsmqBindingexactlyOnce=truedeadLetterQueue=system />         </netMsmqBinding>     </bindings>     <client>         <endpointname=SubscriberXAccountEventNotification             address=netmsmq://localhost/private/SubscriberX/accounteventnotificationsvc             binding=netMsmqBindingbindingConfiguration=TransactionalMsmqBinding             contract=MyProjectContractsIAccountEventNotification />             <endpointname=SubscriberYAccountEventNotification             address=netmsmq://localhost/private/SubscriberY/accounteventnotificationsvc             binding=netMsmqBindingbindingConfiguration=TransactionalMsmqBinding             contract=MyProjectContractsIAccountEventNotification />     </client> </systemserviceModel>

  上述配置沒什麼特別的地方 – 需要關注的是 exactlyOnce=true 設置這是事務隊列必須的設置另外就是 netmsmq:// 地址語法這是NetMsmqBinding 協議所需要的私有隊列分別為 SubscriberX/accounteventnotificationsvc 和 SubscriberY/accountnotificationsvc為什麼我給隊列這樣愚蠢的命名呢?繼續讀下面內容

  承載和配置訂閱者
    在過去如果說創建MSMQ客戶端是煩人的那麼創建MSMQ服務更是噩夢你不得不創建你自己的host(一般而言為Windows Service)或者使用一些靈活的MSMQ觸發器功能然後你需要做很多工作確保你的服務沒有丟失消息或者沒有被poison messages所阻塞因為poison message錯誤的消息體(malformed payload)會不斷導致你的服務失敗

  就像在客戶端一樣WCF需要很多工作在服務端 – 但是這些不是直接幫助承載服務和監聽隊列幸運的是這一問題由Windows Vista和Windows server 中提供的IIS 和Windows Activation Services (WAS) 輕松解決IIS 負責監聽MSMQ / tcp / Named Pipes並且激活WCF 服務就像 IIS 監聽HTTP一樣聽起來不錯但是需要提醒的是 – 這些需要靈巧的配置

  首先你需要在IIS中設置application 指向service包括svc文件和nfig配置文件這與在IIS 通過HTTP部署service一樣

  接著你需要創建消息隊列你可以通過Vista的Computer Management console 或Windows server 的Server Manager配置隊列的名稱必須匹配application name 加上svc 文件名例如 SubscriberX/accounteventnotificationsvc在創建隊列時確保標記隊列支持事務因為隨後不能改變你也需要設置隊列的權限這樣運行NetMsmq Listener 服務的帳號(缺省為NETWORK SERVICE)能夠接收消息任何運行Client/Publisher 都能夠發送消息(缺省為NETWORK SERVICE)

  最後你需要配置IIS和WAS 的站點和特定application支持NetMsmq 監聽器(在開始操作之前確保你已經安裝了WAS和nonHTTP激活windows組件)最簡單的辦法是使用appcmdexe 命令行(\system\inetsrv目錄下)
    appcmd set site Default Web Site +bindings[protocol=netmsmqbindingInformation=localhost]
    appcmd set app Default Web Site/SubscriberX /enabledProtocols:netmsmq
    配置好IIS後接下來是確保service的WCF配置是正確的如同你期望的那樣這將與客戶端的配置非常相似
    <systemserviceModel>     <bindings>         <netMsmqBinding>             <bindingname=TransactionalMsmqBindingexactlyOnce=truedeadLetterQueue=systemreceiveErrorHandling=Move/>         </netMsmqBinding>     </bindings>     <services>         <servicename=SubscriberXNotificationService>             <endpointcontract=MyProjectContractsIAccountEventNotification                 bindingConfiguration=TransactionalMsmqBinding                 binding=netMsmqBinding                 address=netmsmq://localhost/private/SubscriberX/accounteventnotificationsvc/>         </service>     </services>    </systemserviceModel>

  值得說明的一點是receiveErrorHandling=Move這一屬性可以幫助節省我們一個月的工作這一屬性讓WCF轉移多次重復處理失敗的消息到MSMQ的子隊列poison然後繼續處理下一個消息而不是阻塞服務這一子隊列和遠程隊列事務性讀取等待功能是Vista 和 windows server 中MSMQ 的一些新特性

  實現訂閱者(Subscribers)
    最後一件事是實現訂閱者當然最多的代碼是特定業務邏輯的實現因為我僅僅描述service interface的實現在我們的系統中確保沒有消息丟失是非常重要的既然MSMQ能夠確保消息的到達因此消息不會無緣故的消失事實上大多數消息的丟失是在MSMQ成功傳遞消息到達service 之後有可能service 接收到消息之後在service 成功處理消息之前發生異常導致失敗(可能由於bug或配置問題)避免這一問題最好的辦法是使用事務跨越從隊列接收消息和業務邏輯的處理如果發生失敗將回滾事包括從隊列中接收的消息如果只是一個臨時的小故障消息將再次被成功處理如果問題持續或是錯誤的消息在經過多次嘗試後該消息將被認為是poison 消息如前面提及的該消息將被轉移到poison 子隊列由管理員手動處理

  上述所有的工作非常簡單因為MSMQ和WCF支持所有這些特性(假定你使用事務性隊列)你需要做的工作是由一些attributes標記你的服務實現聲明當消息從隊列取出後業務邏輯應該登記事務

  public class NotificationService : IAccountEventNotification
    {
        [OperationBehavior(TransactionScopeRequired = true TransactionAutoComplete = true)]
        public void OnEventOccurred(AccountEventLog value)
        {
            // Businessspecific logic
        }
    }
    如對上述solution 的實現有疑問或改進建議歡迎到我們的論壇( )進行交流

  結論
    這是我最近最長的blog文章之一這一解決方案非常強大且特別簡單去實現這是由於WCF / MSMQ / IIS 技術的先進性在過去許多人(包括我)花了幾個月的時間盡力去實現pub/sub模式往往不能達到預期的效果現在使用這些的技術消除了大量的定制代碼事實上這篇文章中少量的代碼和配置腳本實現了pub/sub模式

  譯者注
    我們是 開源論壇小組負責 / YAF 開源ASPNET/C# 論壇的開發工作免費提供項目源代碼下載歡迎您訪問下載交流和學習包括Enterprise Message Bus / WCF / MSMQ / BizTalk / SSB / sql server / Net Framework 等等

  下載 / YAF 開源論壇 v (ASPNET/C#)
    x?g=posts&t=

  英文原文
    Building a Pub/Sub Message Bus with WCF and MSMQ by Tom Hollander
    apubsubmessagebuswithwcfandmsmqaspx


From:http://tw.wingwit.com/Article/program/Java/hx/201311/26674.html
  • 上一篇文章:

  • 下一篇文章:
  • 推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.