WCF服務端運行時架構體系詳解[下篇]
作為WCF中一個核心概念,終結點在不同的語境中實際上指代不同的對象。站在服務描述的角度,我們所說的終結點實際上是指ServiceEndpoint對象。如果站在WCF服務端運行時框架來說,終結點實際上指代的是終結點分發器(EndpointDispatcher)。而ServiceEndpoint與EndpointDispatcher是一一匹配的,并且前者是創建后者的基礎。而終結點分發器具有自己的運行,即分發運行時(DispatchRuntime)。
目錄
一、終結點分發器(EndpointDispatcher)
二、分發運行時(DispatchRuntime)
可擴展組件
認證與授權
服務實例上下文
會話關閉通知
同步上下文
消息檢驗
操作與操作選擇
可擴展屬性
授權
審核
事務與會話
未處理操作
SOAP ValidateMustUnderstand處理
并發控制
一、終結點分發器(EndpointDispatcher)
除了之前介紹的三個輔助信道分發器向匹配的終結點分發器實施消息路由的三個屬性(AddressFilter、ContractFilter和FilterPriority)之外,你還可以通過屬性ContractName和ContractNamespace得到服務契約的名稱和命名空間,以通過EndpointAddress屬性得到相應的終結點地址。將消息路由到該終結點分發器的信道分發器可以通過屬性ChannelDispatcher獲得。但是對于終結點分發器來說,其重要的還是通過屬性DispatchRuntime表示的分發運行時。
{
//其他成員
public string ContractName { get; }
public string ContractNamespace { get; }
public MessageFilter AddressFilter { get; set; }
public MessageFilter ContractFilter { get; set; }
public int FilterPriority { get; set; }
public ChannelDispatcher ChannelDispatcher { get; }
public DispatchRuntime DispatchRuntime { get; }
public EndpointAddress EndpointAddress { get; }
}
二、分發運行時(DispatchRuntime)
毫不夸張地說,終結點分發器的分發運行時是WCF整個服務端運行時架構體系的核心,同時也是對WCF服務端服務模型進行擴展重點考慮的對象。分發運行時之所以具有如此重要的地位,原因在于:終結點分發器接收到從信道分發器路由的消息的整個處理是在分發運行時中進行的。
和上面分析信道分發器一樣,我們首先來看看分發運行時具有哪些可擴展的組件。終結點的分發運行時對應的類型為DispatchRuntime。下面的代碼片斷列出了這些擴展組件在DispatchRuntime中的對應的屬性定義。
{
//其他成員
public ServiceAuthorizationManager ServiceAuthorizationManager { get; set; }
public ServiceAuthenticationManager ServiceAuthenticationManager { get; set; }
public RoleProvider RoleProvider { get; set; }
public ReadOnlyCollection<IAuthorizationPolicy> ExternalAuthorizationPolicies { get; set; }
public IInstanceContextProvider InstanceContextProvider { get; set; }
public SynchronizedCollection<IInstanceContextInitializer> InstanceContextInitializers { get; }
public InstanceContext SingletonInstanceContext { get; set; }
public IInstanceProvider InstanceProvider { get; set; }
public SynchronizedCollection<IInputSessionShutdown> InputSessionShutdownHandlers { get; }
public SynchronizationContext SynchronizationContext { get; set; }
public SynchronizedCollection<IDispatchMessageInspector> MessageInspectors { get; }
public SynchronizedKeyedCollection<string, DispatchOperation> Operations { get; }
public IDispatchOperationSelector OperationSelector { get; set; }
}
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。
{
InstanceContext GetExistingInstanceContext(Message message, IContextChannel channel);
void InitializeInstanceContext(InstanceContext instanceContext, Message message, IContextChannel channel);
bool IsIdle(InstanceContext instanceContext);
void NotifyIdle(InstanceContextIdleCallback callback, InstanceContext instanceContext);
}
{
void Initialize(InstanceContext instanceContext, Message message);
}
你可以將任意數量的實例上下文初始化器應用到WCF服務端分發系統中,而DispatchRuntime的只讀屬性InstanceContextInitializers代表了當前的實例上下文初始化器列表。當實例上下文被成功創建后,這些實例上下文初始化器將會以此被執行。
在默認的情況下,WCF會采用反射的方式調用服務類型的無參構造函數來創建服務實例。但是你可以通過自定義一個被稱為實例提供者的組件來讓WCF服務端分發系統按照你希望的方式創建你想要的實例作為最終的服務實例。所有的InstanceProvider都實現具有如下定義的接口System.ServiceModel.Dispatcher.IInstanceProvider,而DispatchRuntime的InstanceProvider屬性表示這個真正被用于提供具體服務實例的實例提供者。《WCF技術剖析(卷1)》第9章《實例管理與會話》中也有關于實例提供者的介紹。
{
object GetInstance(InstanceContext instanceContext);
object GetInstance(InstanceContext instanceContext, Message message);
void ReleaseInstance(InstanceContext instanceContext, object instance);
}
3、會話關閉通知
在一個基于雙工(Duplex)消息交換模式的會話中,如果客戶端在完成了基于當前會話所有消息介紹工作時系統通知服務端以從事一些相關的處理工作,可以通過實現一個被稱為輸入會話關閉處理器(InputSessionShutdownHandler)的組件。該組件類型實現如下一個名為IInputSessionShutdown的接口。DoneReceiving方法會在接受到上述通知時被調用,而輸入參數調用當前的雙工信道。如果該信道出現錯誤(狀態變成Faulted),方法ChannelFaulted會被調用。
{
void ChannelFaulted(IDuplexContextChannel channel);
void DoneReceiving(IDuplexContextChannel channel);
}
4、同步上下文
在默認的情況下,如果服務寄宿過程中的當前線程具有同步上下文(比如將Windows Forms應用作為服務的宿主,主線程具有一個類型為WindowsFormsSynchronizationContext的同步上下文),那么后續的處理將在該同步上下文中進行。而你可以通過DispatchRuntime的SynchronizationContext屬性得到該同步上下文對象。
這就意味著,所有的處理均是按照同步的方式進行處理的,在高并發的情況下著往往是致命的。我們可以使用在服務類型上應用ServiceBehaviorAttribute特性并通過指定UseSynchronizationContext屬性決定是否使用宿主線程當前的同步上下文。
public sealed class ServiceBehaviorAttribute : Attribute, IServiceBehavior
{
//其他成員
public bool UseSynchronizationContext { get; set; }
}
5、消息檢驗
WCF允許你對服務端框架進行擴展以實現對路由道終結點分發器的消息進行后續的處理,我們把這個機制成為消息檢驗。比如說,如果說客戶端希望向服務端傳輸一些與功能無關的上下文信息,可以將其封裝成消息報頭并添加到請求消息中。你就可以通過這個消息檢驗機制將上下文信息從相應的消息報頭中獲取出來。此外,通過消息的檢驗機制運行你對傳入的消息進行相應的更改。如果后續處理中需要相應的控制信息,你可以將其通過該機制將這些信息以消息報頭的方式至于傳輸的消息之中。
消息的檢驗機制通過自定義消息檢驗器(MessageInspector)。實際上WCF的客戶端和服務端運行時具有自己的消息檢驗器;客戶端的被稱為客戶端消息檢驗器(ClientMessageInspector),用于針對發出的請求消息和接收的回復消息進行檢驗;而服務端的被稱為分發消息檢驗器(DispatchMessageInspector),用于針對于接收的請求消息和發送的回復消息進行檢驗。在這里我們主要討論分發消息檢驗器,在介紹客戶端運行時架構的時候我們會對客戶端消息檢驗器進行相應的介紹。
分發消息檢驗器實現了接口IDispatchMessageInspector。從如下給出的IDispatchMessageInspector接口的定義中我們可以看出,該接口具有兩個方法BeforeSendReply和AfterReceiveRequest。從方法名稱就可以看出來,它們分別在請求消息被接收后以及回復消息被發送前對消息進行檢驗。
{
object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext);
void BeforeSendReply(ref Message reply, object correlationState);
}
分發運行時具有一個分發消息檢驗器的列表,該列表通過屬性MessageInspectors表示。根據具體應用中針對消息檢查的需要,你可以在該列表中添加任意的分發消息檢驗器,它們按照添加的順序依次執行。
6、操作與操作選擇
我們說了,服務端分發體系對消息請求處理最終體現在多相應操作方法的執行。在服務表示中,操作通過類型OperationDescription表示。當服務端運行時框架通過服務描述被創建的時候,每一個OperationDescription會轉變成DiaptchOperation對象。而DispatchRuntime的Operations屬性就代表當前終結點的所有DispatchOperation集合。這是一個類似于字典的集合類型,而代表鍵值的字符串為操作的名稱。
由于當前的分發運行時中大都具有多個DispatchOperation對象,而它接收的是一個消息,那么必須具有某種機制以實現根據接收的消息解析出對應的目標操作。這樣一種操作的選擇機制在WCF分發運行時中是通過一個被稱為操作選擇器(OperationSelector)的組件來實現的。操作選擇器對應的接口為IDispatchOperationSelector,針對消息對操作的選擇通過SelectOperation實現,方法的返回值代表操作的名稱。當從該方法得到正確的操作名稱,WCF就可以從Operations熟悉代表的操作列表中選擇正確的DispatchOperation了。
{
string SelectOperation(ref Message message);
}
介紹了分發運行時可供擴展(添加或者替換)的組件之后,我們來看它具有哪些可以修改的屬性,通過修改這些屬性會對整個消息分發、實例上下文的激活以及服務操作的執行等行為具有怎樣的影響。下面的代碼片斷列出了這些屬性。
{
//其他成員
public PrincipalPermissionMode PrincipalPermissionMode { get; set; }
public bool ImpersonateCallerForAllOperations { get; set; }
public AuditLogLocation SecurityAuditLogLocation { get; set; }
public bool SuppressAuditFailure { get; set; }
public AuditLevel ServiceAuthorizationAuditLevel { get; set; }
public AuditLevel MessageAuthenticationAuditLevel { get; set; }
public bool AutomaticInputSessionShutdown { get; set; }
public bool ReleaseServiceInstanceOnTransactionComplete { get; set; }
public bool TransactionAutoCompleteOnSessionClose { get; set; }
publicbool IgnoreTransactionMessageProperty { get; set; }
public DispatchOperation UnhandledDispatchOperation { get; set; }
public bool ValidateMustUnderstand { get; set; }
public ConcurrencyMode ConcurrencyMode { get; set; }
}
7、授權
DispatchRuntime的屬性PrincipalPermissionMode和ImpersonateCallerForAllOperations是與授權相關的兩個屬性。前者表示安全主體權限模式,后者表示是否以模擬的客戶端Windows帳號執行所有的操作。它們對應于服務行為ServiceAuthorizationBehavior的同名屬性。
{
//其他成員
public bool ImpersonateCallerForAllOperations { get; set; }
public PrincipalPermissionMode PrincipalPermissionMode { get; set; }
}
8、審核
DispatchRuntime具有四個與審核相關的屬性。其中SecurityAuditLogLocation決定了審核日志被入何處;SuppressAuditFailure決定了是否要抑制審核日志記錄過程中出現的非關鍵異常的拋出;而ServiceAuthorizationAuditLevel和MessageAuthenticationAuditLevel則表示具體那些認證和授權相關的事件應該進行審核日志的記錄。這四個屬性對應于服務行為ServiceSecurityAuditBehavior的同名屬性。
{
//其他成員
public AuditLogLocation AuditLogLocation { get; set; }
public bool SuppressAuditFailure { get; set; }
public AuditLevel MessageAuthenticationAuditLevel { get; set; }
public AuditLevel ServiceAuthorizationAuditLevel { get; set; }
}
9、事務與會話
接下來介紹四個與會話和事務相關的屬性。AutomaticInputSessionShutdown表示服務端是否在客戶端關閉輸出會話(Output Session)的時候是否關閉輸入會話(Input Session)。關于會話的相關內容,在《WCF技術剖析(卷1)》第9章《實例管理與會話》中具有詳細的介紹。至于這里指的輸出和輸入則是消息交換模式(MEP:Message Exchange Pattern),你可以從《WCF技術剖析(卷1)》的第4章《服務契約》中找到關于消息交換模式的詳細介紹。
另外兩個屬性ReleaseServiceInstanceOnTransactionComplete和TransactionAutoCompleteOnSessionClose表示在事務提交之后是否自定釋放服務實例,以及在會話關閉之后是否自動提交事務。上述的兩個屬性分別對應ServiceBehaviorAttribute的同名屬性。而AutomaticInputSessionShutdown屬性則定于ServiceBehaviorAttribute的AutomaticSessionShutdown屬性。
public sealed class ServiceBehaviorAttribute : Attribute, IServiceBehavior
{
//其他成員
public bool AutomaticSessionShutdown { get; set; }
public bool ReleaseServiceInstanceOnTransactionComplete { get; set; }
public bool TransactionAutoCompleteOnSessionClose { get; set; }
}
為了實現基于消息交換的事務傳播,事務本身是被封裝成一個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特性的同名屬性。
public sealed class ServiceBehaviorAttribute : Attribute, IServiceBehavior
{
//其他成員
public bool ValidateMustUnderstand { get; set; }
}
12、并發控制
最后一個屬性ConcurrencyMode與并發有關,用于指定三種并發模式(Single、Reentrant和Multiple)中的某一種。它同樣對應于ServiceBehaviorAttribute特性的同名屬性。
public sealed class ServiceBehaviorAttribute : Attribute, IServiceBehavior
{
//其他成員
public ConcurrencyMode ConcurrencyMode { get; set; }
}
留言列表