跟我一起學WCF(11)——WCF中隊列服務詳解


一、引言

   在前面的WCF服務中,它都要求服務與客戶端兩端都必須啟動並且運行,從而實現彼此間的交互。然而,還有相當多的情況希望一個面向服務的應用中擁有離線交互的能力。WCF通過服務隊列的方法來支持客戶端和服務之間的離線工作,客戶端將消息發送到一個隊列中,再由服務對它們進行處理。下面讓我們具體看看WCF中的隊列服務。

二、WCF隊列服務的優勢

   在介紹WCF隊列服務之前,首先需要了解微軟消息隊列(MSMQ)。MSMQ是在多個不同應用之間實現相互通信的一種異步傳輸模式,相互通信的應用可以分布在同一台機器,也可以分布在相連的網絡環境。它的實現原理是:客戶端將消息發送到一個容器中,然后把它保存到一個系統公用空間的消息隊列(Message Queue)中,本地或異地的服務再從該隊列中取出發送給它的消息進行處理。更多詳細內容可以參考我的博文:跟我一起學WCF(1)——MSMQ消息隊列

  WCF框架對MSMQ進行了集成和擴展,MSMQ支持離線消息模式,並且在WCF框架下,提供了基於http橋的internet網絡隊列服務的調用擴展。從而賦予了WCF隊列服務以下幾點優勢:

  1. 支持離線消息模式。因為WCF框架集成了MSMQ,所以WCF隊列服務自然也支持離線消息。
  2. 支持將操作分解。WCF支持將工作分解為多個操作放入隊列中,可改善系統的可用性和吞吐量。
  3. 提供對失敗的事務做善后處理。當我們的業務事務需要幾個小時或幾天完成的時候,我們通常將它分為至少2個事務。第一個事務將需要立即完成的工作放入隊列,而第二個事務用於驗證第一個事務是否成功,並在必要的情況下對失敗的事務進行善后處理。
  4. 支持負載平衡。可以把過載的客戶端請求放入隊列,空閑的時候進行處理,這樣可以平衡系統的吞吐量,改善性能。

三、WCF隊列服務通信框架

   WCF使用NetMsmqBinding來支持消息隊列通信。當客戶端調用服務時,客戶端消息會被封裝為MSMQ消息,發送到系統公用的消息隊列中,服務宿主在運行狀態下會啟動通道監聽器來檢測消息隊列消息,如果發現對應的消息,則會從隊列里取出消息,使用分發器轉發給對應的服務,具體的通信框架如下圖所示:

  如果宿主離線,消息會被放入隊列,等待下一次宿主聯機時,在執行消息分發給指定WCF服務處理。

  另外WCF還提供了MsmqIntegrationBinding類,該類用於需要將WCF 應用和現有的基於MSMQ的應用集成的情況。WCF應用可利用該綁定向現有的MSMQ應用程序發生消息,或從這些應用程序接收消息。

四、利用WCF隊列服務來實現離線操作

   前面介紹WCF隊列服務的優勢和它的通信框架,下面具體通過一個例子來詮釋WCF隊列服務的實現。我們還是按照前面文章介紹的三個步驟來實現該實例。

  第一步:定義契約和實現服務。具體的實現代碼如下所示:

 1 [ServiceContract]
 2     public interface IWCFMSMQService
 3     {
 4         // 操作契約,必須為單向操作
 5         [OperationContract(IsOneWay = true)]
 6         void SayHello(string message);
 7     }
 8 
 9 // 契約實現
10     public class WCFMSMQService : IWCFMSMQService
11     {
12         public WCFMSMQService()
13         {
14             Console.WriteLine("WCF MSMQ Service instance was created at: {0}", DateTime.Now);
15         }
16 
17         #region IOrderProcessor Members
18 
19         [OperationBehavior(TransactionScopeRequired = true, TransactionAutoComplete = true)]
20         public void SayHello(string message)
21         {
22             Console.WriteLine("Hello! {0},調用WCF操作的時間為:{1}", message, DateTime.Now);
23         }
24 
25         #endregion
26     }

  上面代碼需要注意一點:WCF操作必須定義為單向操作,因為要實現的是一個隊列服務,其特點為異步、離線,無返回值。所以要設置IsOneWay屬性為true。

  第二步:實現宿主。這里仍然使用控制台應用程序作為WCF隊列服務的宿主,具體的實現代碼如下所示:

 1 namespace WCFConsoleHost
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             string path = @".\private$\LearningHardWCFMSMQ";
 8             if (!MessageQueue.Exists(path))
 9             {
10                 MessageQueue.Create(path, true);
11             }
12 
13             using (ServiceHost host = new ServiceHost(typeof(WCFMSMQService)))
14             {
15                 host.Opened += delegate
16                 {
17                     Console.WriteLine("Service has begun to listen\n\n");
18                 };
19 
20                 host.Open();
21 
22                 Console.Read();
23             }
24         }
25     }
26 }

  對應的配置信息如下所示:

<configuration>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="msmqServiceBehavior">
          <serviceMetadata httpGetEnabled="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <netMsmqBinding>
        <binding name="msmqBinding">
          <security>
            <transport msmqAuthenticationMode="None" msmqProtectionLevel="None"/>
            <message clientCredentialType="None"/>
          </security>
        </binding>
      </netMsmqBinding>
    </bindings>
    <services>
      <service behaviorConfiguration="msmqServiceBehavior" name="WCFContractAndService.WCFMSMQService">
        <endpoint address="net.msmq://localhost/private/LearningHardWCFMSMQ" binding="netMsmqBinding"
          bindingConfiguration="msmqBinding" contract="WCFContractAndService.IWCFMSMQService" />
        <!--發布服務元數據的終結點-->
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:9003/" />
          </baseAddresses>
        </host>
      </service>
    </services>
  </system.serviceModel>
</configuration>

  第三步:WCF客戶端的實現。首先以管理員權限啟動宿主,然后通過添加服務引用的方式來生成代理客戶端類,具體在添加服務引用窗口地址欄輸入:http://localhost:9003/mex來添加服務引用,添加服務引用成功后將生成代理類,通過代理類來對WCF服務進行訪問,具體的實現代碼如下所示:

 1 namespace WCFClient
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             WCFMSMQServiceClient proxy = new WCFMSMQServiceClient("NetMsmqBinding_IWCFMSMQService");
 8             using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
 9             {
10                 Console.WriteLine("WCF First Call at:{0}", DateTime.Now);
11                 proxy.SayHello("World");
12 
13                 Thread.Sleep(2000);//客戶端休眠兩秒,繼續下一次調用
14                 Console.WriteLine("WCF Second Call at:{0}", DateTime.Now);
15                 proxy.SayHello("Learning Hard");
16 
17                 scope.Complete();
18             }       
19 
20             Console.Read();
21         }
22     }
23 }

  經過上面三步,我們就完成了一個WCF隊列服務程序。下面讓我們看看該程序的運行結果。

  因為WCF隊列服務是對MSMQ的集成和擴展,所以此時該程序客戶端可以在WCF服務離線的情況也可運行,即WCF服務宿主不啟動,客戶端也可以正常運行,這點與前面介紹的WCF程序完成不同,因為前面的WCF程序,如果宿主程序不啟動而直接啟動客戶端程序,則客戶端程序運行時將會發生異常。該程序之所以不會發生異常的原因在於,此時客戶端程序是直接與消息隊列進行交互的,而不是直接與WCF服務進行交互。此時只運行WCF客戶端,你將看到如下圖所示的運行結果:

  同時,你在消息隊列的專有隊列的隊列消息中將看到兩條未處理的消息信息,具體效果如下圖所示:

  因為WCF服務宿主還沒有啟動,所以客戶端發送的消息信息還沒有被取出處理,接下來,我們啟動下服務宿主來對隊列中的消息進行處理,此時你可以選擇關閉客戶端(當然你也可以不關閉)。具體的WCF服務宿主的運行結果如下圖所示:

  此時,如果刷新消息隊列的專有隊列中的隊列消息,你將看不到任何消息了,這是因為WCF服務從消息隊列取出消息進行處理了,即消息完成了出隊的操作。

五、總結

   到這里,WCF隊列服務的介紹也就結束了。本文主要介紹了WCF集成了MSMQ的功能,WCF可以利用netMsmqBinding來實現離線服務。其實現程序與MSMQ程序實現差不多,關於MSMQ的相關內容也可以參考我的另一篇博文:跟我一起學WCF(1)——MSMQ消息隊列。在下一篇博文中將分享WCF對Rest服務的支持和實現。

  本文所有源碼:WCFMSMQService.zip

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM