实践重于理论——创建一个监控程序探测WCF的并发处理机制
由于WCF的并發(fā)是針對某個(gè)封裝了服務(wù)實(shí)例的InstanceContext而言的(參考《并發(fā)的本質(zhì)》《并發(fā)中的同步》),所以在不同的實(shí)例上下文模式下,會表現(xiàn)出不同的并發(fā)行為。接下來,我們從具體的實(shí)例上下文模式的角度來剖析WCF的并發(fā)處理機(jī)制,如果對WCF實(shí)例上下文模式和實(shí)例上下文提供機(jī)制不了解的話,請參閱《WCF技術(shù)剖析(卷1)》第9章。
為了使讀者對采用不同實(shí)例上下文對并發(fā)的影響有一個(gè)深刻的認(rèn)識,會創(chuàng)建一個(gè)簡單的WCF應(yīng)用,并在此基礎(chǔ)上添加監(jiān)控功能,主要監(jiān)控各種事件的執(zhí)行時(shí)間,比如客戶端服務(wù)調(diào)用的開始和結(jié)束時(shí)間,服務(wù)操作開始執(zhí)行和結(jié)束執(zhí)行的時(shí)間等等。讀者可以根據(jù)實(shí)時(shí)輸出的監(jiān)控信息,對WCF的并發(fā)處理情況有一個(gè)很直觀的認(rèn)識。 [源代碼從這里下載]
一、服務(wù)契約定義
本實(shí)例依然采用我們熟悉的四層結(jié)構(gòu),即契約、服務(wù)、寄宿和客戶端。為了以可視化的形式實(shí)時(shí)輸出監(jiān)控信息,對于客戶端和服務(wù)寄宿程序均采用Windows Form應(yīng)用類型。我們依然以計(jì)算服務(wù)作為例子,下面是服務(wù)契約的定義。
1: using System.ServiceModel; 2: namespace Artech.ConcurrentServiceInvocation.Service.Interface 3: { 4: [ServiceContract(Namespace="http://www.artech.com/")] 5: public interface ICalculator 6: { 7: [OperationContract] 8: double Add(double x, double y); 9: } 10: }二、創(chuàng)建監(jiān)控器:EventMonitor
由于我們需要監(jiān)控各種事件的時(shí)間,所以我定義了一個(gè)名為EventType的枚舉表示不同的事件類型。8個(gè)枚舉值分別表示開始和結(jié)束服務(wù)調(diào)用(客戶端)、開始和結(jié)束服務(wù)操作執(zhí)行(服務(wù)端)、開始和結(jié)束回調(diào)(服務(wù)端)以及開始和結(jié)束回調(diào)操作的執(zhí)行(客戶端)。關(guān)于回調(diào)的事件枚舉選項(xiàng)在本例中不會需要,主要是為了后續(xù)演示的需要。
1: using System; 2: namespace Artech.ConcurrentServiceInvocation.Service.Interface 3: { 4: public enum EventType 5: { 6: StartCall, 7: EndCall, 8: StartExecute, 9: EndExecute, 10: StartCallback, 11: EndCallback, 12: StartExecuteCallback, 13: EndExecuteCallback 14: } 15: }然后我定義了如下一個(gè)EventMonitor的靜態(tài)類,該類通過兩個(gè)重載的Send方法觸發(fā)事件的形式發(fā)送事件通知。我定義了專門的事件參數(shù)類型MonitorEventArgs,封裝客戶端ID、事件類型和觸發(fā)時(shí)間。Send具有兩個(gè)重載,一個(gè)具有用整數(shù)表示的客戶端ID,另一個(gè)沒有。前者用于客戶端,可以顯式指定客戶端ID,后者需要從客戶端手工添加的消息報(bào)頭提取客戶端ID,該消息報(bào)頭的名稱和命名空間通過兩個(gè)常量定義。
1: using System; 2: using System.ServiceModel; 3: namespace Artech.ConcurrentServiceInvocation.Service.Interface 4: { 5: public static class EventMonitor 6: { 7: public const string CientIdHeaderNamespace = "http://www.artech.com/"; 8: public const string CientIdHeaderLocalName = "ClientId"; 9: public static EventHandler<MonitorEventArgs> MonitoringNotificationSended; 10:? 11: public static void Send(EventType eventType) 12: { 13: if (null != MonitoringNotificationSended) 14: { 15: int clientId = OperationContext.Current.IncomingMessageHeaders.GetHeader<int>(CientIdHeaderLocalName,CientIdHeaderNamespace); 16: MonitoringNotificationSended(null,new MonitorEventArgs(clientId,eventType,DateTime.Now)); 17: } 18: } 19:? 20: public static void Send(int clientId, EventType eventType) 21: { 22: if (null != MonitoringNotificationSended) 23: { 24: MonitoringNotificationSended(null,new MonitorEventArgs(clientId,eventType,DateTime.Now)); 25: } 26: } 27: } 28:? 29: public class MonitorEventArgs : EventArgs 30: { 31: public int ClientId{ get; private set; } 32: public EventType EventType{ get; private set; } 33: public DateTime EventTime{ get; private set; } 34:? 35: public MonitorEventArgs(int clientId, EventType eventType, DateTime eventTime) 36: { 37: this.ClientId = clientId; 38: this.EventType = eventType; 39: this.EventTime = eventTime; 40: } 41: } 42: }三、創(chuàng)建服務(wù)類型:CalculatorService
EventMonitor的Send方法可以直接用在CalculatorService的Add操作方法中,實(shí)時(shí)輸出操作方法開始和結(jié)束執(zhí)行的時(shí)間,已經(jīng)當(dāng)前處理的客戶端的ID。下面的代碼是CalculatorService的定義,需要注意的是我通過ServiceBehaviorAttribute將UseSynchronizationContext屬性設(shè)置成False,至于為什么需要這么做,是后續(xù)文章需要講述的內(nèi)容。服務(wù)操作Add通過將當(dāng)前線程掛起5秒鐘,用以模擬一個(gè)相對耗時(shí)的操作,便于我們更好的通過監(jiān)控輸出的時(shí)間分析并發(fā)處理的情況。
1: using System.ServiceModel; 2: using System.Threading; 3: using Artech.ConcurrentServiceInvocation.Service.Interface; 4: namespace Artech.ConcurrentServiceInvocation.Service 5: { 6: [ServiceBehavior(UseSynchronizationContext = false)] 7: public class CalculatorService : ICalculator 8: { 9: public double Add(double x, double y) 10: { 11: EventMonitor.Send(EventType.StartExecute); 12: Thread.Sleep(5000); 13: double result = x + y; 14: EventMonitor.Send(EventType.EndExecute); 15: return result; 16: } 17: } 18: }四、通過Windows Forms應(yīng)用寄宿服務(wù)
然后,我們在一個(gè)Windows Form應(yīng)用中對上面創(chuàng)建的CalculatorService進(jìn)行寄宿,并將該應(yīng)用作為服務(wù)端的監(jiān)控器。在這個(gè)應(yīng)用中,我只添加了如圖1所示的簡單的窗體,整個(gè)窗體僅僅有一個(gè)唯一的ListBox控件,在運(yùn)行的是時(shí)候相應(yīng)的監(jiān)控信息就實(shí)時(shí)地逐條追加到該ListBox之中。
圖1 服務(wù)端監(jiān)控窗體設(shè)計(jì)界面
我們通過注冊EventMonitor的靜態(tài)MonitoringNotificationSended事件的形式實(shí)時(shí)輸出服務(wù)端監(jiān)控信息。同時(shí),對CalculatorService的寄宿實(shí)現(xiàn)在監(jiān)控窗體的Load事件中,整個(gè)窗體后臺代碼如下所示。
1: using System; 2: using System.ServiceModel; 3: using System.Threading; 4: using System.Windows.Forms; 5: using Artech.ConcurrentServiceInvocation.Service; 6: using Artech.ConcurrentServiceInvocation.Service.Interface; 7: namespace Artech.ConcurrentServiceInvocation.Hosting 8: { 9: public partial class MonitorForm : Form 10: { 11: private SynchronizationContext _syncContext; 12: private ServiceHost _serviceHost; 13:? 14: public MonitorForm() 15: { 16: InitializeComponent(); 17: } 18:? 19: private void MonitorForm_Load(object sender, EventArgs e) 20: { 21: string header = string.Format("{0, -13}{1, -22}{2}", "Client", "Time", "Event"); 22: this.listBoxExecutionProgress.Items.Add(header); 23: _syncContext = SynchronizationContext.Current; 24: EventMonitor.MonitoringNotificationSended += ReceiveMonitoringNotification; 25: this.Disposed += delegate 26: { 27: EventMonitor.MonitoringNotificationSended -= ReceiveMonitoringNotification; 28: _serviceHost.Close(); 29: }; 30: _serviceHost = new ServiceHost(typeof(CalculatorService)); 31: _serviceHost.Open(); 32: } 33:? 34: public void ReceiveMonitoringNotification(object sender, MonitorEventArgs args) 35: { 36: string message = string.Format("{0, -15}{1, -20}{2}", args.ClientId, args.EventTime.ToLongTimeString(), args.EventType); 37: _syncContext.Post(state => this.listBoxExecutionProgress.Items.Add(message), null); 38: } 39: } 40: }下面是WCF相關(guān)的配置,我們采用WS2007HttpBinding作為終結(jié)點(diǎn)的綁定類型。
1: <?xml version="1.0" encoding="utf-8" ?> 2: <configuration> 3: <system.serviceModel> 4: <services> 5: <service name="Artech.ConcurrentServiceInvocation.Service.CalculatorService"> 6: <endpoint address="http://127.0.0.1:3721/calculatorservice" binding="ws2007HttpBinding" contract="Artech.ConcurrentServiceInvocation.Service.Interface.ICalculator" /> 7: </service> 8: </services> 9: </system.serviceModel> 10: </configuration>五、創(chuàng)建客戶端程序
最后我們編寫客戶端程序,這也是一個(gè)Windows Form應(yīng)用。該應(yīng)用既作為CalculatorService的客戶端程序而存在,同時(shí)也是客戶端的監(jiān)控器。整個(gè)應(yīng)用具有一個(gè)與圖1一樣的窗體。同樣以注冊EventMonitor的靜態(tài)MonitoringNotificationSended事件的形式實(shí)時(shí)輸出客戶端監(jiān)控信息。在監(jiān)控窗體的Load時(shí)間中,利用ThreadPool創(chuàng)建5個(gè)服務(wù)代理以并發(fā)的形式進(jìn)行服務(wù)調(diào)用。這五個(gè)服務(wù)代理對象對應(yīng)的客戶端ID分別為從1到5,并通過消息報(bào)頭的形式發(fā)送到服務(wù)端。整個(gè)監(jiān)控窗體的代碼如下所示,相應(yīng)的配置就不在列出來了。
1: using System; 2: using System.ServiceModel; 3: using System.Threading; 4: using System.Windows.Forms; 5: using Artech.ConcurrentServiceInvocation.Service.Interface; 6: namespace Artech.ConcurrentServiceInvocation.Client 7: { 8: public partial class MonitorForm : Form 9: { 10: private SynchronizationContext _syncContext; 11: private ChannelFactory<ICalculator> _channelFactory; 12: private static int clientIdIndex = 0; 13:? 14: public MonitorForm() 15: { 16: InitializeComponent(); 17: } 18:? 19: private void MonitorForm_Load(object sender, EventArgs e) 20: { 21: string header = string.Format("{0, -13}{1, -22}{2}", "Client", "Time", "Event"); 22: this.listBoxExecutionProgress.Items.Add(header); 23: _syncContext = SynchronizationContext.Current; 24: _channelFactory = new ChannelFactory<ICalculator>("calculatorservice"); 25:? 26: EventMonitor.MonitoringNotificationSended += ReceiveMonitoringNotification; 27: this.Disposed += delegate 28: { 29: EventMonitor.MonitoringNotificationSended -= ReceiveMonitoringNotification; 30: _channelFactory.Close(); 31: }; 32:? 33: for (int i = 1; i <= 5; i++) 34: { 35: ThreadPool.QueueUserWorkItem(state => 36: { 37: int clientId = Interlocked.Increment(ref clientIdIndex); 38: ICalculator proxy = _channelFactory.CreateChannel(); 39: using (proxy as IDisposable) 40: { 41: EventMonitor.Send(clientId, EventType.StartCall); 42: using (OperationContextScope contextScope = new OperationContextScope(proxy as IContextChannel)) 43: { 44: MessageHeader<int> messageHeader = new MessageHeader<int>(clientId); 45: OperationContext.Current.OutgoingMessageHeaders.Add(messageHeader.GetUntypedHeader(EventMonitor.CientIdHeaderLocalName, EventMonitor.CientIdHeaderNamespace)); 46: proxy.Add(1, 2); 47: } 48: EventMonitor.Send(clientId, EventType.EndCall); 49: } 50: }, null); 51: } 52: } 53:? 54: public void ReceiveMonitoringNotification(object sender, MonitorEventArgs args) 55: { 56: string message = string.Format("{0, -15}{1, -20}{2}", args.ClientId, args.EventTime.ToLongTimeString(), args.EventType); 57: _syncContext.Post(state => this.listBoxExecutionProgress.Items.Add(message), null); 58: } 59: } 60: }到此為止,我們的監(jiān)控程序就完成了。接下來我將借助于這么一個(gè)監(jiān)控程序?qū)χv述不同的實(shí)例上下文模式、不同的并發(fā)模式、以及并發(fā)請求基于相同或者不同的代理的情況下,最終會表現(xiàn)出怎樣的并發(fā)處理行為。比如在ConcurrencyMode.Single + InstanceContextMode.Single的情況下,客戶端和服務(wù)端將會輸出如圖2所示的監(jiān)控信息,從中我們會看出并發(fā)的請求最終卻是以串行化執(zhí)行的。具體分析,請關(guān)注下篇。
圖2 ConcurrencyMode.Single + InstanceContextMode.Single條件下并發(fā)事件監(jiān)控輸出
轉(zhuǎn)載于:https://www.cnblogs.com/lzjsky/archive/2011/04/08/2009221.html
總結(jié)
以上是生活随笔為你收集整理的实践重于理论——创建一个监控程序探测WCF的并发处理机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vs2008编译中文变量名的lua解释器
- 下一篇: ascx页面获取标签的通用方法