閱讀20 返回首頁    go 阿裏雲 go 技術社區[雲棲]


使命必達: 深入剖析WCF的可靠會話[編程篇](下)

整個可靠會話的機製是完全在信道層實現的,而整個信道層的最終締造者就是綁定,所以可靠會話編程是圍繞著綁定進行的。《上篇》對實現可靠會話的綁定元素已經如何使用係統綁定實現可靠會話進行了介紹,下篇將和你探討WCF可靠會話編程模型餘下兩個主題:自定義綁定對消息傳遞的強製約束

一、為自定義綁定的可靠會話進行設置

綁定是一係列綁定元素的有序組合,但是係統綁定為我們提供適應了某種典型通信環境的綁定元素組合方式,可以看成是“套餐”。但是,如果套餐不符合您的胃口,你應該查看菜單點你喜歡的菜肴。自定義綁定給了你最大的自由度,是能能夠根據具體的通信環境自由組合需要的綁定元素。

關於可靠會話,如果你采用係統綁定,你定製的範圍其實很窄(僅限於InactivityTimeout和Ordered屬性)。但是,如果你采用自定義綁定,由於你操作的對象就是ReliableSessionBindingElement綁定元素,所有你可以對所有的選項進行自由配置。雖然我們可以通過編程的方式之間將創建的ReliableSessionBindingElement對象添加到綁定的綁定元素集合中,但是我們還是強烈建議你通過配置的方式來對可靠會話的相關選項進行定製。為了讓讀者能夠了解某個特性的配置,我個人覺得最好的辦法就是直接讓讀者看看相關配置節的定義。WCF將ReliableSessionBindingElement的配置定義在如下所示的ReliableSessionElement類型中。通過ReliableSessionElement,你不但可以了解可靠會話相關的配置屬性,還可以了解到其他相關的配置信息,比如最大值、最小值和默認值等。你可以驗證一下它們是否和我們前麵的介紹一致。

   1: public sealed class ReliableSessionElement : BindingElementExtensionElement
   2: {
   3:    [ConfigurationProperty("acknowledgementInterval", DefaultValue="00:00:00.2"), TypeConverter(typeof(TimeSpanOrInfiniteConverter)), ServiceModelTimeSpanValidator(MinValueString="00:00:00.0000001")]
   4:     public TimeSpan AcknowledgementInterval { get; set; }
   5:     public override Type BindingElementType { get; }
   6:     [ConfigurationProperty("flowControlEnabled", DefaultValue=true)]
   7:     public bool FlowControlEnabled { get; set; }
   8:     [ServiceModelTimeSpanValidator(MinValueString="00:00:00.0000001"), TypeConverter(typeof(TimeSpanOrInfiniteConverter)), ConfigurationProperty("inactivityTimeout", DefaultValue="00:10:00")]
   9:     public TimeSpan InactivityTimeout { get; set; }
  10:     [IntegerValidator(MinValue=1, MaxValue=0x4000), ConfigurationProperty("maxPendingChannels", DefaultValue=4)]
  11:     public int MaxPendingChannels { get; set; }
  12:     [IntegerValidator(MinValue=1), ConfigurationProperty("maxRetryCount", DefaultValue=8)]
  13:     public int MaxRetryCount { get; set; }
  14:     [ConfigurationProperty("maxTransferWindowSize", DefaultValue=8), IntegerValidator(MinValue=1, MaxValue=0x1000)]
  15:     public int MaxTransferWindowSize { get; set; }
  16:     [ConfigurationProperty("ordered", DefaultValue=true)]
  17:     public bool Ordered { get; set; }
  18:     protected override ConfigurationPropertyCollection Properties { get; }
  19:     [TypeConverter(typeof(ReliableMessagingVersionConverter)), ConfigurationProperty("reliableMessagingVersion", DefaultValue="WSReliableMessagingFebruary2005")]
  20:     public ReliableMessagingVersion ReliableMessagingVersion { get; set; }
  21: }

在對自定義綁定進行配置的時候,我們隻需要在綁定元素集合中添加ReliableSessionElement配置元素,並對相應的配置屬性進行設置即可。下麵的XML是服務端的WCF配置,我們采用自定義綁定作為終結點綁定。該自定義綁定由三個綁定元素組成,通過TextMessageEncodingElement對消息進行基於文本編碼;通過HttpTransportBindingElement采用HTTP協議進行傳輸;在兩者之間的ReliableSessionBindingElement實現了對消息的可靠傳輸。在bindings/binding/reliableSession配置節中,我對所有的屬性進行的顯式設置。

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <configuration> 
   3:   <system.serviceModel>    
   4:     <bindings>
   5:       <customBinding>
   6:         <binding name="reliableSessionBinding">
   7:           <textMessageEncoding />
   8:           <reliableSession acknowledgementInterval="00:00:02" flowControlEnabled="false" inactivityTimeout="00:20:00" maxPendingChannels="8" maxRetryCount="4" maxTransferWindowSize="16" reliableMessagingVersion="WSReliableMessaging11" />
   9:           <httpTransport />
  10:         </binding>
  11:       </customBinding>     
  12:     </bindings>
  13:     <services>
  14:       <service name="Artech.MessageInspection.Receiver.CalculatorService">
  15:         <endpoint address="https://www.artech.com/calculatorservice"
  16:             binding="customBinding" bindingConfiguration="reliableSessionBinding"
  17:             contract="Artech.MessageInspection.Receiver.ICalculator" />
  18:       </service>
  19:     </services>
  20:   </system.serviceModel>
  21: </configuration>

看到這裏,我想有的讀者會問這麼一個問題:一個綁定可以有一係列綁定元素構成,那麼ReliableSessionBindingElement應該置於何處呢?要搞清楚這個問題,需要對WCF的綁定模型有一個大致的了解。綁定的目的創建一個用於處理和傳輸消息的信道棧,信道在信道棧的順序決定於對應的綁定元素的排列順序。可靠會話將客戶端和服務端通過ReliableSessionBindingElement創建的可靠信道作為分界線,並在它們之間提供消息可靠傳輸保障。也就是說,信道棧中位於可靠信道之下(靠近傳輸信道)部分存在於可靠會話的勢力範圍之中,而上麵部分則脫離可靠會話的管轄範圍。這也是在前麵給出的實例中為何我們將用於模擬不可靠網絡的綁定元素配置到ReliableSessionBindingElement之下的原因所在。

由於在實際的應用中,我們主要通過可靠會話為網絡傳輸的不穩定性提供可靠傳輸的保障,所以我們一般將ReliableSessionBindingElement配置到傳輸綁定元素之上。至於消息編碼綁定元素,是置於ReliableSessionBindingElement之上或者之下均沒有關係。如果你認真閱讀過《WCF技術剖析(卷1)》第3章,你會知道消息編碼綁定元素並不參與信道的創建,而是將編碼的方式傳入綁定上下文,傳輸信道據此采用相應的編碼方式進行消息的編碼或者解碼。此外,為了,保證可靠會話的安全性,我們需要將可靠會話綁定到一個通過安全會話信道提供的安全上下文中。在這種情況下,ReliableSessionBindingElement需要位於安全綁定元素之上。

我們說WCF可靠會話編程完全就是圍繞著綁定進行的,可以說得更加具體點,是圍繞著ReliableSessionBindingElement進行的。除了對係統綁定或者自定義綁定進行設置,關於可靠會話編程模型,還涉及到一個契約行為:DeliveryRequirementsAttribute,我們可以利用DeliveryRequirementsAttribute對服務契約進行一些基於可靠會話的強製性約束。

二、通過DeliveryRequirementsAttribute對可靠會話進行強製約束

DeliveryRequirementsAttribute這個自定義特性,實際上是一個契約行為。我們可以將其應用到服務契約類型或者服務類型上,強製要求相應終結點綁定必須滿足設定的關於消息傳輸方麵的要求。DeliveryRequirementsAttribute定義如下,其中忽略了實現IContractBehavior接口的4個方法。

   1: [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class, AllowMultiple=true)]
   2: public sealed class DeliveryRequirementsAttribute : Attribute, IContractBehavior, IContractBehaviorAttribute
   3: {
   4:     //其他成員
   5:     public QueuedDeliveryRequirementsMode QueuedDeliveryRequirements { get; set; }
   6:     public bool RequireOrderedDelivery { get; set; }
   7:     public Type TargetContract { get; set; }
   8: }

DeliveryRequirementsAttribute定義了兩個屬性QueuedDeliveryRequirements和RequireOrderedDelivery,分別代表相應的終結點綁定必須滿足的兩個要求:隊列傳遞和有序交付。隊列傳遞及采用消息隊列(即MSMQ)的機製進行消息傳遞,在下一個係列中我們會對隊列服務進行單獨介紹。而有序交付就是本章涉及的可靠消息傳輸的有序交付。IContractBehavior屬性是對IContractBehaviorAttribute的實現,當我們將DeliveryRequirementsAttribute特性應用到某個實現了多個服務契約的服務上時,可以指定設置的消息傳遞要求是針對某個服務契約。

在服務端,當基於服務類型創建的ServiceHost對象被開啟的時候,如果相應終結點綁定無法滿足通過將DeliveryRequirementsAttribute特性應用到服務契約類型或者服務類型上設置的關於隊列傳輸或者有序交付的要求時,會拋出一個InvalidOperationException異常。如果將DeliveryRequirementsAttribute特性應用到服務契約上,客戶端在試圖開啟ChannelFactory<TChannel>對象的時候,同樣會驗證用於服務調用的終結點綁定是否滿足相應的要求,如果無法滿足,同樣會拋出一個InvalidOperationException異常。

舉個例子,假設我們定義如下一個IOrderService的服務契約用於處理訂單。在IOrderService接口上,應用了DeliveryRequirementsAttribute特性並將RequireOrderedDelivery設置成True,要求終結點綁定強製開啟可靠消息的有序交付特性。

   1: [ServiceContract(Namespace = "https://www.artech.com/")]
   2: [DeliveryRequirements(RequireOrderedDelivery = true)]
   3: public interface IOrderService
   4: {
   5:     [OperationContract]
   6:     void Process(Order order);
   7: }

現在我們對實現該服務契約的服務進行寄宿,相應的配置如下。從中我們可以看到,我們采用WS2007HttpBinding作為終結點的綁定。通過綁定配置,開啟了可靠會話,但是將ordered屬性配置成False。

   1: ?xml version="1.0" encoding="utf-8" ?>
   2: <configuration>
   3:   <system.serviceModel>   
   4:     <bindings>      
   5:       <ws2007HttpBinding>
   6:         <binding name="reliableSessionHttpBinding">
   7:           <reliableSession ordered="false" enabled="true" />
   8:          </binding>
   9:       </ws2007HttpBinding>
  10:     </bindings>
  11:     <services>
  12:       <service name="Artech.MessageInspection.Receiver.OrderService">
  13:         <endpoint address="https://127.0.0.1:3721/OrderService" binding="ws2007HttpBinding"
  14:           bindingConfiguration="reliableSessionHttpBinding" contract="Artech.MessageInspection.Receiver.IOrderService" />
  15:       </service>
  16:     </services>
  17:   </system.serviceModel>
  18: </configuration>

當進行服務寄宿的時候,你會得到如圖1所示的InvalidOperationException異常。當你進一步看清具體的異常消息的時候,你可能第一感覺就是作者把圖片弄錯了。因為終結點綁定部滿足DeliveryRequirementsAttribute設定的關於有序交付的要求,和隊列傳遞根據就不相關。但是圖1就是真實運行後的截圖,這是WCF自身的一個Bug。在《WCF 中關於可靠會話的BUG!!》這篇文章中有對該Bug的原因的深入探討。

clip_image002

圖1 終結點綁定不能滿足DeliveryRequirementsAttribute設定的要求導致的異常

到此為止,關於WCF可靠會話編程方麵的內容就到此為止,下一篇我們對可靠會話架構體係進行進一步的挖掘,對可靠會話具體的實現原理進行深入剖析,敬請期待。


作者:蔣金楠
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
原文鏈接

最後更新:2017-10-27 14:34:45

  上一篇:go  WCF中關於可靠會話的BUG!!
  下一篇:go  使命必達: 深入剖析WCF的可靠會話[原理揭秘篇](上)