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


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

在《實例篇》給出的例子中,我實際上是通過對終結點的綁定進行相應的配置讓整個消息的交換過程在一個可靠會話中進行,進而實現可靠消息傳輸的目的。由於整個可靠會話的機製是完全在信道層實現的,而整個信道層的最終締造者就是綁定,所以可靠會話編程是圍繞著綁定進行的。而從結構組成的角度講,綁定本質上就是一組綁定元素的有序集合,沒有個為了實現各自的目的對傳入信道棧的消息進行消息的處理。在這裏,實現可靠會話是一個特殊的綁定元素:ReliableSessionBindingElement.

一、從ReliableSessionBindingElement談起

WCF中整個可靠會話的實現完全是通過ReliableSessionBindingElement這個一個綁定元素創建的信道實現的。具體的實現機製,我們會在《原理篇》進行單獨的介紹,在這裏我們僅僅討論可靠會話編程的部分。所以我們先來看看ReliableSessionBindingElement屬性定義部分,因為可靠會話實現的行為是受這些屬性控製的。

   1: public sealed class ReliableSessionBindingElement : BindingElement, IPolicyExportExtension
   2: {  
   3:     //其他成員
   4:     public TimeSpan AcknowledgementInterval { get; set; }
   5:     public bool FlowControlEnabled { get; set; }
   6:     public TimeSpan InactivityTimeout { get; set; }
   7:     public int MaxPendingChannels { get; set; }
   8:     public int MaxRetryCount { get; set; }
   9:     public int MaxTransferWindowSize { get; set; }
  10:     public bool Ordered { get; set; }
  11:     public ReliableMessagingVersion ReliableMessagingVersion { get; set; }
  12: }

雖然我們還沒有深入到對可靠會話具體實現機製的介紹,但是通過前麵對WS-RM可靠消息傳輸模型的討論,相信讀者對可靠會話的實現機製會有一個大致的了解。在這裏我們可以通過簡單地幾句話對可靠會話實現機製進行簡單的介紹。

WCF的可靠會話是建立在客戶端和服務端之間,確保消息可靠傳輸的上下文,相當於WS-RM中序列的概念。在消息發送端和接收端具有一個消息緩衝區(或者稱為消息窗口)對消息進行緩存,前者緩存已經發送但是尚未接收到確認的消息,後者緩存尚未向上交付的消息。消息在發送之前,會被賦予一個特殊的SOAP報頭,其中包含表示消息在整個可靠會話生命周期內被發送的序號,並保存到消息緩衝區中。消息被接收端成功接收之後,會向發送端發送一個確認消息表示具有某個序號的消息已經成功接收。如果需要保障有序交付,接收端在交付之前需要確定先於該消息發送的所有消息是否成功交付。如果是則實施交付,否則將其放入消息緩衝區,等待之前消息的抵達。當之前所有消息被成功接收之後,接收端按照消息序號從小到大的順序對消息實施交付。緩存的消息被成功交付後,會從緩衝區移除。 而消息發送端在接收到消息確認之後,會根據消息序號將對應的消息從緩衝區衝移出。如果在限定的超時時限內沒有接收到以發消息的確認,會認為該消息發送失敗,該消息會從緩衝區中提取出來並重新發送。

那麼,現在我們了解了WCF可靠會話大體機製的情況下來了解一下ReliableSessionBindingElement各個屬性的含義。

  • AcknowledgementInterval如果消息的發送方和接收方通過雙工通道連接,接收方能夠隨時向接收方發送確認。在這種情況下,為了降低網絡流量,WCF采用批量確認的機製。也就是說,當接收端成功接收到某個消息的時候,並不會立即針對該消息發送確認,而是等待一定時間後,對所有接收到的消息進行批量確認。AcknowledgementInterval表示消息接收端發送確認之前等待的時間間隔,默認為0.2秒(200毫秒)。該設置僅僅針對於NetTcpBingding和WSDualHttpBiding等支持雙工通信的綁定有效,而像BasicHttpBinding和WSHttpBiding這樣的綁定無此設置。

注:

  • FlowControlEnabled該屬性指示可靠會話是否已啟用流控製(Flow Control)。流控製是可靠會話實現的一種機製,目的是確保發送方所發送的消息數不超過接收方可處理的消息數。接收方擁有消息緩衝區,用於容納突增的消息和無序的消息。接收方在每次確認時都會告知發送方此緩衝區中的剩餘空間量。利用此信息,發送方就可以在接收方緩衝區中沒有剩餘空間時,停止發送新的消息。FlowControlEnabled默認值為True。
  • InactivityTimeout在WS-RM中,被創建的RM序列具有一個Expires屬性表示序列的生命周期。但是,我們不能通過可靠會話的整個生命周期的時限,也就是,整個可靠會話並沒有具體時間的限製。但是,WCF並不能依賴於可靠會話被顯式地被終止,而設定一個超時時限,在該時間範圍內如何沒有活動的消息交換,WCF會將可靠會話關閉。InactivityTimeout就是這麼一個表示可靠會話在關閉之前保持非活動狀態的時間間隔,默認值為10分鍾。
  • MaxPendingChannels通道在等待被接受時處於掛起狀態。一旦達到該限製,就不會創建任何通 道並將其置於掛起模式,直到此數值降低(通過接受掛起的通道)。這是對每個偵聽器的限製。當達到此閾值時如果遠程應用程序嚐試建立新的可靠會話,則會拒絕請求且打開操作將提示此錯誤。這麼一個可靠會話期間可為掛起狀態的最大通道數通過MaxPendingChannels表示,默認值為4;
  • MaxRetryCount如果在某個可接受時間範圍內,消息的發送端沒有接收到某個以發消息的確認,會對該消息進行重傳。MaxRetryCount表示重傳的次數,最小值為1,最大值為0x7FFFFFFF(Int32.MaxValue)的默認值為8。WCF使用指數補償算法根據計算的平均往返時間來確定何時重新傳輸。初始重試時間是根據測量的創建會話往返時間來計算的。重新傳輸算法是每進行一次嚐試,延遲時間就會增加一倍。如此一來,從第一次傳輸嚐試到最後一次傳輸嚐試大約會曆時 8.5 分鍾。可以根據計算的往返時間來調整第一次嚐試重新傳輸的時間,因此這些嚐試所經曆的時間將會相應地發生變化。這樣,可以使重新傳輸時間動態地適應不斷變化的網絡條件。MaxRetryCount表示進行重傳的次數,在達到此限製之前未接收到確認被視為嚴重的通信故障,這種情況將引發通道出錯的事件。
  • MaxTransferWindowSize該屬性表示消息緩衝區的大小,或者說是緩衝區能夠容納消息的數量。MaxTransferWindowSize最小值為1,最大值為4096,默認值為32。MaxTransferWindowSize屬性的值可在發送方和接收方進行設置。如果達到發送方的這個限製,則會阻止其他發送調用。如果達到接收方的這個限製,則不會接受到達基礎通道的新消息。可靠會話會使用窗口 協議來幫助提高網絡利用率。所需的緩衝量直接從傳輸窗口的大小派生而來。調整此值時請注意,最佳傳輸窗口直接與帶寬和延遲相關。理想的最大窗口大小是帶寬乘以延遲。如果比此值小,網絡利用率就會低於 100%,如果比此值大,就會浪費空間。
  • Ordered該屬性表示是否啟用“有序傳輸”機製確保消息的接收端完全按照消息被發送的順序進行交付;默認值為False;
  • ReliableMessagingVersion該屬性表示可靠會話支持的WS-RM的版本。在WCF中,該WS-RM通過ReliableMessagingVersion類型表示(不是枚舉)。ReliableMessagingVersion定義如下,我們可以通過兩個靜態隻讀屬性WSReliableMessagingFebruary2005和WSReliableMessaging11得到兩個表示WS-RM 1.0和WS-RM 1.1的ReliableMessagingVersion對象。靜態隻讀屬性Default表示默認的WS-RM版本,其值目前和WSReliableMessagingFebruary2005屬性一致。
   1: public abstract class ReliableMessagingVersion
   2: {
   3:     public static ReliableMessagingVersion Default { get; }
   4:     public static ReliableMessagingVersion WSReliableMessagingFebruary2005 { get; }
   5:     public static ReliableMessagingVersion WSReliableMessaging11 { get; }
   6: }

對ReliableSessionBindingElement綁定元素有了一定的了解之後,我們來談談包含該綁定元素的綁定。先來了解一下係統綁定。

二、為係統綁定的可靠會話進行設置

在眾多係統綁定中,有很大一部分都為可靠會話提供支持,比如WSHttpBinding、WS2007HttpBinding、NetTcpBinding、WSFederationHttpBinding、WS2007FederationHttpBinding和WSDualHttpBinding。這些綁定類型中均有一個的名稱為ReliableSession的屬性,屬性類型為ReliableSession或者其子類OptionalReliableSession。下麵的代碼演示了WSHttpBindingBase(WSHttpBinding和WS2007HttpBinding的基類)和WSDualHttpBinding的ReliableSession屬性的定義。

   1: public abstract class WSHttpBindingBase : Binding, IBindingRuntimePreferences
   2: {
   3:     //其它成員
   4:     public OptionalReliableSession ReliableSession { get; }
   5: }
   6:  
   7: public class WSDualHttpBinding : Binding, IBindingRuntimePreferences
   8: {
   9:     //其它成員
  10:     public ReliableSession ReliableSession { get; }
  11: }

在討論不同類型的係統綁定對可靠會話的支持之前,我們不妨先來看看ReliableSession和OptionalReliableSession的定義。從下麵的定義,我們可以看出ReliableSession定義非常簡單。僅僅具有兩個可讀寫的屬性InactivityTimeout和Ordered,不同多說讀者也知道這和前麵介紹的ReliableSessionBindingElement的同名屬性相匹配,而整個ReliableSession對象是根據ReliableSessionBindingElement對象創建的。ReliableSession的子類OptionalReliableSession多了一個額外可讀寫的屬性Enabled,這是一個讓綁定啟用可靠會話的開關。

   1: public class ReliableSession
   2: {  
   3:     public ReliableSession(ReliableSessionBindingElement reliableSessionBindingElement);
   4:     public TimeSpan InactivityTimeout { get; set; }
   5:     public bool Ordered { get; set; }
   6: }
   7:  
   8: public class OptionalReliableSession : ReliableSession
   9: {   
  10:     public OptionalReliableSession(ReliableSessionBindingElement reliableSessionBindingElement);
  11:     public bool Enabled { get; set; }
  12: }

雖然在ReliableSessionBindingElement綁定元素中為我們定義了眾多控製可靠會話行為的屬性,但是ReliableSession中僅僅為我們公布了其中兩個:InactivityTimeout和Ordered。潛在的信心告訴我們,對於這些支持可靠會話的係統綁定來說,我們隻能設置可靠會話在關閉之前保持非活動狀態的時間間隔和開啟或者關閉有序交付特性。其他選項,比如支持的WS-RM版本,以及消息緩衝區大小,都是係統為我們定製的,不能修改。

對於前麵提到的若幹支持可靠會話的係統綁定,除了WSDualHttpBinding的ReliableSession屬性類型為ReliableSession外,其餘的均為OptionalReliableSession。也就是我們不能關閉WSDualHttpBinding的可靠會話特性,它總是按照可靠會話的機製進行消息的交換。WCF之所以如此涉及,是源於WSDualHttpBinding支持雙工通信的特殊機製決定的。由於基於請求/回複模式的HTTP傳輸不能夠獨立提供對雙工通信的支持,WCF采用的是雙通道的方式。也就是說,對於通過WSDualHttpBinding創建的所謂的雙工通道是由兩個方向相反的HTTP連接組成的,WCF采用可靠會話機製提供對這兩個連接的匹配。

除了InactivityTimeout和Ordered兩個屬性可以進行設置之外,定義在ReliableSessionBindingElement綁定元素中的各個屬性大多采用默認值。但是,有一個例外,即表示支持WS-RM版本的ReliableMessagingVersion屬性。對於WSHttpBinding、WSDualHttpBinding和WSFederationHttpBinding支持的版本是WS-RM 1.0,而WS2007HttpBinding和WS2007FederationHttpBinding則支持的是WS-RM 1.1。

我們可以通過編程的方式開啟或者關閉終結點使用的除WSDualHttpBinding之外的其他係統綁定(限於支持可靠會話係統綁定)的可靠會話開關,以及設置InactivityTimeout和Ordered屬性。不過,我們最好還是采用配置的方式對可靠會話進行設置。可靠會話相關配置定義在具體綁定配置中的reliableSession結點中。下麵的配置中,我們在客戶端對終結點使用的WS2007HttpBinding的可靠會話進行了設置。

   1: <system.serviceModel>
   2:     <bindings>
   3:       <ws2007HttpBinding>
   4:         <binding name="reliableSession2007Binding">
   5:           <reliableSession enabled="True" inactivityTimeout="00:20:00"  ordered="True"/>
   6:           <security mode="None"/>
   7:         </binding>
   8:       </ws2007HttpBinding>
   9:     </bindings>
  10:       <client >
  11:         <endpoint name="calculatorservice" address="https://127.0.0.1/calculatorservice" binding="ws2007HttpBinding" bindingConfiguration="reliableSession2007Binding" contract="Artech.MessageInspection.Sender.ICalculator" />
  12:       </client>
  13:   </system.serviceModel>

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


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

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

  上一篇:go  使命必達: 深入剖析WCF的可靠會話[協議篇](下)
  下一篇:go  又一個中國方案輸出全世界!天貓雙11“五個全球”讓世界小得像街