WCF服務端運行時架構體係詳解[下篇]
作為WCF中一個核心概念,終結點在不同的語境中實際上指代不同的對象。站在服務描述的角度,我們所說的終結點實際上是指ServiceEndpoint對象。如果站在WCF服務端運行時框架來說,終結點實際上指代的是終結點分發器(EndpointDispatcher)。而ServiceEndpoint與EndpointDispatcher是一一匹配的,並且前者是創建後者的基礎。而終結點分發器具有自己的運行,即分發運行時(DispatchRuntime)。
目錄
一、終結點分發器(EndpointDispatcher)
二、分發運行時(DispatchRuntime)
可擴展組件
認證與授權
服務實例上下文
會話關閉通知
同步上下文
消息檢驗
操作與操作選擇
可擴展屬性
授權
審核
事務與會話
未處理操作
SOAP ValidateMustUnderstand處理
並發控製
除了之前介紹的三個輔助信道分發器向匹配的終結點分發器實施消息路由的三個屬性(AddressFilter、ContractFilter和FilterPriority)之外,你還可以通過屬性ContractName和ContractNamespace得到服務契約的名稱和命名空間,以通過EndpointAddress屬性得到相應的終結點地址。將消息路由到該終結點分發器的信道分發器可以通過屬性ChannelDispatcher獲得。但是對於終結點分發器來說,其重要的還是通過屬性DispatchRuntime表示的分發運行時。
1: public class EndpointDispatcher
2: {
3: //其他成員
4: public string ContractName { get; }
5: public string ContractNamespace { get; }
6:
7: public MessageFilter AddressFilter { get; set; }
8: public MessageFilter ContractFilter { get; set; }
9: public int FilterPriority { get; set; }
10:
11: public ChannelDispatcher ChannelDispatcher { get; }
12: public DispatchRuntime DispatchRuntime { get; }
13: public EndpointAddress EndpointAddress { get; }
14: }
毫不誇張地說,終結點分發器的分發運行時是WCF整個服務端運行時架構體係的核心,同時也是對WCF服務端服務模型進行擴展重點考慮的對象。分發運行時之所以具有如此重要的地位,原因在於:終結點分發器接收到從信道分發器路由的消息的整個處理是在分發運行時中進行的。
和上麵分析信道分發器一樣,我們首先來看看分發運行時具有哪些可擴展的組件。終結點的分發運行時對應的類型為DispatchRuntime。下麵的代碼片斷列出了這些擴展組件在DispatchRuntime中的對應的屬性定義。
1: public sealed class DispatchRuntime
2: {
3: //其他成員
4: public ServiceAuthorizationManager ServiceAuthorizationManager { get; set; }
5: public ServiceAuthenticationManager ServiceAuthenticationManager { get; set; }
6: public RoleProvider RoleProvider { get; set; }
7: public ReadOnlyCollection<IAuthorizationPolicy> ExternalAuthorizationPolicies { get; set; }
8:
9: public IInstanceContextProvider InstanceContextProvider { get; set; }
10: public SynchronizedCollection<IInstanceContextInitializer> InstanceContextInitializers { get; }
11: public InstanceContext SingletonInstanceContext { get; set; }
12: public IInstanceProvider InstanceProvider { get; set; }
13:
14: public SynchronizedCollection<IInputSessionShutdown> InputSessionShutdownHandlers { get; }
15: public SynchronizationContext SynchronizationContext { get; set; }
16:
17: public SynchronizedCollection<IDispatchMessageInspector> MessageInspectors { get; }
18:
19: public SynchronizedKeyedCollection<string, DispatchOperation> Operations { get; }
20: public IDispatchOperationSelector OperationSelector { get; set; }
21: }
1、認證與授權
分發運行時具有一組與安全相關的組件,具體來說是這組組件是與身份認證和授權相關。它們包括用於進行認證的ServiceAuthorizationManager,用於進行授權的ServiceAuthorizationManager,以及在在ASP.NET Roles安全主體權限模式下實現授權采用的RoleProvider和在自定義安全主體權限模式下自定義的授權策略(通過ExternalAuthorizationPolicies屬性表示)。如果你閱讀了《深入剖析授權在WCF中的實現[共14篇]》,相對對這四個對象不會感到陌生。
2、服務實例上下文
服務端框架對服務調用請求的處理最終必然體現在服務實例的創建和操作方法的調用。而服務實例並不是單獨存儲,而是存在於一個上下文中,該上下文被稱為實例上下文(InstanceContext)。WCF服務端框架通過一個被稱為實例上下文提供者(InstanceContextProvider)來提供基於當前服務請求對應的實例上下文。這裏所說的實例上下文的提供機製包括兩種情況下:創建新的服務上下文,或者提供一個現有之前創建好的實例上下文。
實例上下文通過類型InstanceContext表示,而所有的實例上下文提供者實現了一個具有如下定義的接口IInstanceContextProvider。WCF為我們提供了相應的實例上下文提供者以實現不同的實例上下文模式:單調(PerCall)、會話(PerSession)和單例(Single)。關於實例上下文、實例上下文模式以及它們最終采用怎樣的實例上下文提供者,在《WCF技術剖析(卷1)》第9章《實例管理與會話》有詳細的介紹。在這裏你需要了解的是:WCF服務端框建最終使用的實例上下文提供者反映在InstanceContextProvider屬性上。如果采用單例實例上下文模式,最終提供的實例上下文賦值給了屬性SingletonInstanceContext。
1: public interface IInstanceContextProvider
2: {
3: InstanceContext GetExistingInstanceContext(Message message, IContextChannel channel);
4: void InitializeInstanceContext(InstanceContext instanceContext, Message message, IContextChannel channel);
5: bool IsIdle(InstanceContext instanceContext);
6: void NotifyIdle(InstanceContextIdleCallback callback, InstanceContext instanceContext);
7: }
當實例上下文被創建出來後,如果你需要對其進行一些初始化操作。比如通過檢查被創建出來的實例上下文並執行相應的一些操作,或者直接對創建的實例上下文作相應的修改。實例上下文的初始化工作通過一個被稱作實例上下文初始化器(InstanceContextInitializer)來實現,它們的類型實現了一個具有如下定義的接口IInstanceContextInitializer。相應的初始化操作定義在Initialize方法中。
1: public interface IInstanceContextInitializer
2: {
3: void Initialize(InstanceContext instanceContext, Message message);
4: }
你可以將任意數量的實例上下文初始化器應用到WCF服務端分發係統中,而DispatchRuntime的隻讀屬性InstanceContextInitializers代表了當前的實例上下文初始化器列表。當實例上下文被成功創建後,這些實例上下文初始化器將會以此被執行。
在默認的情況下,WCF會采用反射的方式調用服務類型的無參構造函數來創建服務實例。但是你可以通過自定義一個被稱為實例提供者的組件來讓WCF服務端分發係統按照你希望的方式創建你想要的實例作為最終的服務實例。所有的InstanceProvider都實現具有如下定義的接口System.ServiceModel.Dispatcher.IInstanceProvider,而DispatchRuntime的InstanceProvider屬性表示這個真正被用於提供具體服務實例的實例提供者。《WCF技術剖析(卷1)》第9章《實例管理與會話》中也有關於實例提供者的介紹。
1: public interface IInstanceProvider
2: {
3: object GetInstance(InstanceContext instanceContext);
4: object GetInstance(InstanceContext instanceContext, Message message);
5: void ReleaseInstance(InstanceContext instanceContext, object instance);
6: }
3、會話關閉通知
在一個基於雙工(Duplex)消息交換模式的會話中,如果客戶端在完成了基於當前會話所有消息介紹工作時係統通知服務端以從事一些相關的處理工作,可以通過實現一個被稱為輸入會話關閉處理器(InputSessionShutdownHandler)的組件。該組件類型實現如下一個名為IInputSessionShutdown的接口。DoneReceiving方法會在接受到上述通知時被調用,而輸入參數調用當前的雙工信道。如果該信道出現錯誤(狀態變成Faulted),方法ChannelFaulted會被調用。
1: public interface IInputSessionShutdown
2: {
3: void ChannelFaulted(IDuplexContextChannel channel);
4: void DoneReceiving(IDuplexContextChannel channel);
5: }
和實例上下文初始化器一樣,輸入會話關閉處理器的數量也沒有限製,你可以將多個這樣的組件應用到當前分發運行時。而DispatchRumtime的隻讀屬性InputSessionShutdownHandlers代表當前的輸入會話關閉處理器列表。
4、同步上下文
在默認的情況下,如果服務寄宿過程中的當前線程具有同步上下文(比如將Windows Forms應用作為服務的宿主,主線程具有一個類型為WindowsFormsSynchronizationContext的同步上下文),那麼後續的處理將在該同步上下文中進行。而你可以通過DispatchRuntime的SynchronizationContext屬性得到該同步上下文對象。
這就意味著,所有的處理均是按照同步的方式進行處理的,在高並發的情況下著往往是致命的。我們可以使用在服務類型上應用ServiceBehaviorAttribute特性並通過指定UseSynchronizationContext屬性決定是否使用宿主線程當前的同步上下文。
1: [AttributeUsage(AttributeTargets.Class)]
2: public sealed class ServiceBehaviorAttribute : Attribute, IServiceBehavior
3: {
4: //其他成員
5: public bool UseSynchronizationContext { get; set; }
6: }
此外,由於DispatchRuntime的SynchronizationContext屬性是可讀可寫的,如果你為其指定一個特定的同步上下文對象,相應的處理工作也是在該同步上下文中執行。
5、消息檢驗
WCF允許你對服務端框架進行擴展以實現對路由道終結點分發器的消息進行後續的處理,我們把這個機製成為消息檢驗。比如說,如果說客戶端希望向服務端傳輸一些與功能無關的上下文信息,可以將其封裝成消息報頭並添加到請求消息中。你就可以通過這個消息檢驗機製將上下文信息從相應的消息報頭中獲取出來。此外,通過消息的檢驗機製運行你對傳入的消息進行相應的更改。如果後續處理中需要相應的控製信息,你可以將其通過該機製將這些信息以消息報頭的方式至於傳輸的消息之中。
消息的檢驗機製通過自定義消息檢驗器(MessageInspector)。實際上WCF的客戶端和服務端運行時具有自己的消息檢驗器;客戶端的被稱為客戶端消息檢驗器(ClientMessageInspector),用於針對發出的請求消息和接收的回複消息進行檢驗;而服務端的被稱為分發消息檢驗器(DispatchMessageInspector),用於針對於接收的請求消息和發送的回複消息進行檢驗。在這裏我們主要討論分發消息檢驗器,在介紹客戶端運行時架構的時候我們會對客戶端消息檢驗器進行相應的介紹。
分發消息檢驗器實現了接口IDispatchMessageInspector。從如下給出的IDispatchMessageInspector接口的定義中我們可以看出,該接口具有兩個方法BeforeSendReply和AfterReceiveRequest。從方法名稱就可以看出來,它們分別在請求消息被接收後以及回複消息被發送前對消息進行檢驗。
1: public interface IDispatchMessageInspector
2: {
3: object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext);
4: void BeforeSendReply(ref Message reply, object correlationState);
5: }
分發運行時具有一個分發消息檢驗器的列表,該列表通過屬性MessageInspectors表示。根據具體應用中針對消息檢查的需要,你可以在該列表中添加任意的分發消息檢驗器,它們按照添加的順序依次執行。
6、操作與操作選擇
我們說了,服務端分發體係對消息請求處理最終體現在多相應操作方法的執行。在服務表示中,操作通過類型OperationDescription表示。當服務端運行時框架通過服務描述被創建的時候,每一個OperationDescription會轉變成DiaptchOperation對象。而DispatchRuntime的Operations屬性就代表當前終結點的所有DispatchOperation集合。這是一個類似於字典的集合類型,而代表鍵值的字符串為操作的名稱。
由於當前的分發運行時中大都具有多個DispatchOperation對象,而它接收的是一個消息,那麼必須具有某種機製以實現根據接收的消息解析出對應的目標操作。這樣一種操作的選擇機製在WCF分發運行時中是通過一個被稱為操作選擇器(OperationSelector)的組件來實現的。操作選擇器對應的接口為IDispatchOperationSelector,針對消息對操作的選擇通過SelectOperation實現,方法的返回值代表操作的名稱。當從該方法得到正確的操作名稱,WCF就可以從Operations熟悉代表的操作列表中選擇正確的DispatchOperation了。
1: public interface IDispatchOperationSelector
2: {
3: string SelectOperation(ref Message message);
4: }
至此,我們對分發運行時中基本的可供擴展的組件進行了接收。這些組件是你針對服務端運行時進行擴展的核心。整個分發運行時連同這些可擴展的組件可以通過下圖表示。
介紹了分發運行時可供擴展(添加或者替換)的組件之後,我們來看它具有哪些可以修改的屬性,通過修改這些屬性會對整個消息分發、實例上下文的激活以及服務操作的執行等行為具有怎樣的影響。下麵的代碼片斷列出了這些屬性。
1: public sealed class DispatchRuntime
2: {
3: //其他成員
4: public PrincipalPermissionMode PrincipalPermissionMode { get; set; }
5: public bool ImpersonateCallerForAllOperations { get; set; }
6:
7: public AuditLogLocation SecurityAuditLogLocation { get; set; }
8: public bool SuppressAuditFailure { get; set; }
9: public AuditLevel ServiceAuthorizationAuditLevel { get; set; }
10: public AuditLevel MessageAuthenticationAuditLevel { get; set; }
11:
12:
13: public bool AutomaticInputSessionShutdown { get; set; }
14: public bool ReleaseServiceInstanceOnTransactionComplete { get; set; }
15: public bool TransactionAutoCompleteOnSessionClose { get; set; }
16: public bool IgnoreTransactionMessageProperty { get; set; }
17:
18: public DispatchOperation UnhandledDispatchOperation { get; set; }
19: public bool ValidateMustUnderstand { get; set; }
20: public ConcurrencyMode ConcurrencyMode { get; set; }
21: }
7、授權
DispatchRuntime的屬性PrincipalPermissionMode和ImpersonateCallerForAllOperations是與授權相關的兩個屬性。前者表示安全主體權限模式,後者表示是否以模擬的客戶端Windows帳號執行所有的操作。它們對應於服務行為ServiceAuthorizationBehavior的同名屬性。
1: public sealed class ServiceAuthorizationBehavior : IServiceBehavior
2: {
3: //其他成員
4: public bool ImpersonateCallerForAllOperations { get; set; }
5: public PrincipalPermissionMode PrincipalPermissionMode { get; set; }
6: }
8、審核
DispatchRuntime具有四個與審核相關的屬性。其中SecurityAuditLogLocation決定了審核日誌被入何處;SuppressAuditFailure決定了是否要抑製審核日誌記錄過程中出現的非關鍵異常的拋出;而ServiceAuthorizationAuditLevel和MessageAuthenticationAuditLevel則表示具體那些認證和授權相關的事件應該進行審核日誌的記錄。這四個屬性對應於服務行為ServiceSecurityAuditBehavior的同名屬性。
1: public sealed class ServiceSecurityAuditBehavior : IServiceBehavior
2: {
3: //其他成員
4: public AuditLogLocation AuditLogLocation { get; set; }
5: public bool SuppressAuditFailure { get; set; }
6: public AuditLevel MessageAuthenticationAuditLevel { get; set; }
7: public AuditLevel ServiceAuthorizationAuditLevel { get; set; }
8: }
9、事務與會話
接下來介紹四個與會話和事務相關的屬性。AutomaticInputSessionShutdown表示服務端是否在客戶端關閉輸出會話(Output Session)的時候是否關閉輸入會話(Input Session)。關於會話的相關內容,在《WCF技術剖析(卷1)》第9章《實例管理與會話》中具有詳細的介紹。至於這裏指的輸出和輸入則是消息交換模式(MEP:Message Exchange Pattern),你可以從《WCF技術剖析(卷1)》的第4章《服務契約》中找到關於消息交換模式的詳細介紹。
另外兩個屬性ReleaseServiceInstanceOnTransactionComplete和TransactionAutoCompleteOnSessionClose表示在事務提交之後是否自定釋放服務實例,以及在會話關閉之後是否自動提交事務。上述的兩個屬性分別對應ServiceBehaviorAttribute的同名屬性。而AutomaticInputSessionShutdown屬性則定於ServiceBehaviorAttribute的AutomaticSessionShutdown屬性。
1: [AttributeUsage(AttributeTargets.Class)]
2: public sealed class ServiceBehaviorAttribute : Attribute, IServiceBehavior
3: {
4: //其他成員
5: public bool AutomaticSessionShutdown { get; set; }
6: public bool ReleaseServiceInstanceOnTransactionComplete { get; set; }
7: public bool TransactionAutoCompleteOnSessionClose { get; set; }
8: }
為了實現基於消息交換的事務傳播,事務本身是被封裝成一個TransactionMessageProperty對象並被消息屬性的形式置於消息之中。而DispatchRuntime的IgnoreTransactionMessageProperty屬性表示在接收到這麼一消息的時候,是否要忽略其中的TransactionMessageProperty消息屬性。
是否忽略消息中的IgnoreTransactionMessageProperty屬性決定於終結點的兩個要素,即綁定和契約。具體來說,如果綁定不支持事務流轉(Transaction Flow),則該屬性返回True。反之,還有需要分析服務契約中應用在操作上的TransactionFlowAttribute設置。相關的邏輯,請參閱本書第3章《事務》。
10、未處理操作
但我們在定義服務契約的時候,通過將OperationContractAttribute特性應用在相應的方法上使其成員一個服務操作。每個操作都具有一個Action屬性,並最終決定了針對該操作的請求消息的Action報頭的URI。我們可以通過OperationContractAttribute特性的同名屬性設定操作的Action。如果該屬性沒有在OperationContractAttribute特性進行顯式設置,對應的操作也具有一個默認值。當ServiceHost被開啟之後,每一個終結點對應的操作都轉換成DispatchOperation對象,並添加到DispatchRuntime的Operations屬性表示的操作列表中。
而實際上我們可以通過OperationContractAttribute將操作的Action定義成“*”。這樣的操作被稱為為處理操作(Unhandled Operation)這樣的操作最終同樣會被轉換成DispatchOperation對象,並作為DispatchRuntime的UnhandledDispatchOperation屬性而存在。對於請求的消息,不能從DispatchRuntime的Operations屬性表示的操作列表中找到一個相匹配的操作時,這個未處理操作會被用於選用。
11、SOAP ValidateMustUnderstand處理
DispatchRuntime的ValidateMustUnderstand屬性用於指定是由係統還是由應用程序強製執行 SOAP MustUnderstand標頭處理。使用該屬性來關閉對到達的消息頭強製執行驗證。在正常執行過程中,將消息頭與UnderstoodHeaders 屬性進行比較,來確認是否由服務顯式處理到達的消息。將此屬性設置為false可以禁用此檢查。當設置為false時,應用程序必須檢查具有 MustUnderstand="true" 標記的標頭,如果其中一個或多個標頭沒有被理解,則返回錯誤。當應用程序應接受任何傳入的SOAP 消息(例如,使用類型化消息或非類型化消息)以及執行自定義標頭處理時,這將很有用。該屬性對應於ServiceBehaviorAttribute特性的同名屬性。
1: [AttributeUsage(AttributeTargets.Class)]
2: public sealed class ServiceBehaviorAttribute : Attribute, IServiceBehavior
3: {
4: //其他成員
5: public bool ValidateMustUnderstand { get; set; }
6: }
12、並發控製
最後一個屬性ConcurrencyMode與並發有關,用於指定三種並發模式(Single、Reentrant和Multiple)中的某一種。它同樣對應於ServiceBehaviorAttribute特性的同名屬性。
1: [AttributeUsage(AttributeTargets.Class)]
2: public sealed class ServiceBehaviorAttribute : Attribute, IServiceBehavior
3: {
4: //其他成員
5: public ConcurrencyMode ConcurrencyMode { get; set; }
6: }
WCF服務端運行時架構體係詳解[上篇]
WCF服務端運行時架構體係詳解[中篇]
WCF服務端運行時架構體係詳解[下篇]
WCF服務端運行時架構體係詳解[續篇]
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
最後更新:2017-10-26 16:04:46