概述<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
在本篇隨筆中,通過一些簡單的示例來說一下Web Service中的異步調用模式。調用Web Service方法有兩種方式,同步調用和異步調用。同步調用是程序繼續執行前等候調用的完成,而異步調用在後臺繼續時,程序也繼續執行,不必等待方法處理完成而直接返回。具體的調用流程見下圖:
對於同步調用方法而言,UI線程依賴於方法的實現,方法執行時間過長將導致UI無法及時與用戶進行交互。我們知道,在Windows客戶端中,每個進程都有單一的UI進程,在服務器中,可擴展性依賴於線程的使用。對於異步調用方法而言,能夠及時於用戶交互響應,從而提供了良好的用戶體驗;同時也可以改善服務器的可擴展性,將服務器與通訊問題隔離。
客戶端異步調用方法
在客戶端異步調用是完全基於Proxy的方法,異步行爲最簡單的模式。Visual Studio和WSDL.EXE提供對它的直接支持。所以我們不必在Web服務應用程序中編寫額外的代碼來處理異步調用。
遍及.NET Framework的異步調用有一個基礎的設計模式:Begin方法和End方法,他們分別用於開始和終止異步處理。Visual Studio和WSDL.exe生成了這兩種方法:
Begin<WebServiceMethodName>——該方法通知Web服務開始處理調用,並立即返回。該方法不返回Web服務調用所指定的數據類型,而是返回一種實現IasyncResult接口的數據類型。
End<WebServiceMethodName>——該方法通知Web服務返回先前啓動的Web方法所生成的結果。
IasyncResult接口包含了WaitHandle類型的AsyncWaitHandle特性。這個公共接口允許用戶的客戶應用程序等待調用,而且,該接口將用Any或All語義(例如WaitHandle.WaitOne,WaitAny和WaitAll)作爲信號通知客戶應用程序。例如,如果想要客戶應用程序異步等候一個Web方法,可調用WaitOne來處理要完成的Web服務。
一般來說,客戶端異步代理方法有兩種實現機制:使用同步對象和回調機制(也許你可能對用這個詞不習慣,實在找不到第二個詞來代替,暫且這樣稱呼吧)
同步對象
同步對象允許用戶對Web服務的方法進行調用(使用Begin方法),然後繼續處理。在後面的程序中,可以調用End方法,傳遞同步對象,以便得到調用結果。這種方式下,能夠繼續執行函數中的程序流程,而不執行回調處理。在這裏調用WaitOne()方法會掛起當前線程,避免忙等待的發生,直到Web Services方法調用結束返回後,該線程纔會被重新喚起。
示例代碼:
2/// 利用同步對象實現異步調用
3/// </summary>
4/// <param name="sender"></param>
5/// <param name="e"></param>
6private void btn_AsyncClient_Click(object sender, System.EventArgs e)
7{
8 IAsyncResult ar = wsc.BeginHello(this.txt_UserName.Text, null, null);
9
10
11 MessageBox.Show("Continue to do some other things");
12
13 ar.AsyncWaitHandle.WaitOne();
14
15 strHello = wsc.EndHello(ar);
16
17 this.rtb_Result.Text = strHello;
18}
回調機制
從本質上說,異步回調機制是委託的.NET等價物,它通過在異步操作完成時建立一個被調用的單獨方法來進行工作。調用應用程序能夠繼續處理其他的任務,直到回調函數被調用爲止。這就意味着處理已經完成了,應用程序可以正常運行了。使用同步對象不同於回調機制的區別是,當檢查Web方法是否已經完成,以及檢查Web方法中是否含有需要的結果時,我們無法對其進行控制,而在回調的情況中,Web方法一旦完成,這些工作就會被自動執行。
示例代碼:
2/// 顯示結果
3/// </summary>
4/// <param name="sender"></param>
5/// <param name="e"></param>
6public void UpdateResult(object sender, EventArgs e)
7{
8 this.rtb_Result.Text = strHello;
9}
10
11public void OnHelloComplete(IAsyncResult ar)
12{
13 strHello = wsc.EndHello(ar);
14
15 this.rtb_Result.Invoke(new EventHandler(UpdateResult));
16}
17
18/// <summary>
19/// 用回調機制實現異步調用
20/// </summary>
21/// <param name="sender"></param>
22/// <param name="e"></param>
23private void btn_CallBack_Click(object sender, System.EventArgs e)
24{
25 AsyncCallback cb = new AsyncCallback(OnHelloComplete);
26
27 wsc.BeginHello(this.txt_UserName.Text, cb, null);
28}
使用回調機制還是同步對象取決於用戶所面臨的具體情況。在檢查異步調用是否完成時,如果願意對處理過程進行控制,那麼可以選擇使用同步對象。如果覺得自己編寫代碼來完成對Web服務的調用,且當方法一旦執行完畢就立即由所調用的特殊函數來處理所返回的結果更適合一些,那麼就更適合用回調機制。
在客戶端使用異步方法調用,可以改進UI響應度,在服務器端不需要實現異步操作,對服務器來說是透明的,而且客戶端能夠在任何時間選擇阻塞。
服務端使用Soap One-Way方法
在服務器端使用One-Way方法實現異步調用,其實質是將單項消息發送到端點。這種方式的特點是方法沒有返回值,客戶端方法不會從調用的服務器端方法中收到返回值;我們無法判斷方法結束的時間,對於結果需要顯式通知或者輪詢。
在Web 服務端,我們使用[SoapDocumentMethod]定義One-Way方法:
示例代碼:
2/// One-Way方式的異步調用Set
3/// </summary>
4/// <param name="sender"></param>
5/// <param name="e"></param>
6private void btn_OneWay_Click(object sender, System.EventArgs e)
7{
8 wsc.SetHello(this.txt_UserName.Text);
9}
10
11/// <summary>
12/// One-Way方式的異步調用Get
13/// </summary>
14/// <param name="sender"></param>
15/// <param name="e"></param>
16private void btn_onewayGet_Click(object sender, System.EventArgs e)
17{
18 strHello = wsc.GetHello();
19
20 this.rtb_Result.Text = strHello;
21}
One-Way方法不適合於下列情況:
l 方法需要對結果輪詢
l 方法需要同步
服務端使用WSE SoapSender和SoapRecevier
在進行本部分內容之前,我們需要安裝WSE2.0。WSE支持面向消息的編程,爲我們提供了SoapSender和SoapReceiver基類,它能夠支持發送和接收SoapEnvelopes,同時它也通過SoapClient和SoapService提供了更多的事務支持。SoapSender和SoapReceiver在客戶端和服務端同時實現,客戶端使用SoapSender發送消息,同時可選擇使用SoapReceiver接收消息;服務端使用SoapReceiver接收消息,同時也可以選擇使用SoapSender發送通知和迴應。
示例代碼:
客戶端:
2 /// 自定義的消息接收類
3 /// </summary>
4 public class MyReceiver: SoapReceiver
5 {
6 public static Form1 form;
7 private string strBody;
8
9 protected override void Receive(SoapEnvelope envelope)
10 {
11 strBody = envelope.InnerText;
12
13 ///注意:在進行此項之前,一定要把rtb_Result控件的屬性設爲Public
14 form.rtb_Result.Invoke(new EventHandler(UpdateBody));
15 }
16
17 void UpdateBody(object sender, System.EventArgs e)
18 {
19 form.rtb_Result.Text = strBody;
20 }
21 }
2/// 用WSE實現異步調用
3/// </summary>
4/// <param name="sender"></param>
5/// <param name="e"></param>
6private void button4_Click(object sender, System.EventArgs e)
7{
8 wsc.FireEvent();
9}
Web Service端:
2 {
3 get
4 {
5 return (ArrayList)Application["Listeners"];
6 }
7 }
8
9 [WebMethod]
10 public void AddListener(string listener)
11 {
12 ArrayList alist = (ArrayList)Application["Listeners"];
13
14 if(alist == null)
15 alist = new ArrayList();
16
17 alist.Add(listener);
18
19 Application["Listeners"] = alist;
20
21 }
22
23 [WebMethod]
24 public void FireEvent()
25 {
26 int i;
27
28 for(i = 0;i < this.Listeners.Count;i++)
29 {
30 SoapEnvelope envelope = new SoapEnvelope();
31
32 envelope.SetBodyObject("Hello World!");
33
34 envelope.Context.Addressing.Action = new Action((string)(this.Listeners[i]));
35
36 envelope.Context.Addressing.ReplyTo = new ReplyTo(new System.Uri((string)(this.Listeners[i])));
37
38 SoapSender peerProxy = new SoapSender(new System.Uri((string)(this.Listeners[i])));
39
40 peerProxy.Send(envelope);
41 }
42 }
服務端使用WSE 自定義SoapMSMQ傳輸
SoapMSMQ是一款開源軟件,簡化使用WSE進行MSMQ操作,下載地址:
http://www.codeproject.com/useritems/SoapMSMQ.asp
SoapMSMQ完全支持事務,具有如下特點:
l 在事務中,請求要被同步初始化
l 同步階段排隊請求,並且返回令牌
l 異步階段處理各個事務
l 所有持有令牌的請求都保證會被處理,但可能會不成功
l 支持向客戶端發送通知
對SoapMSMQ感興趣的朋友可以下載下來後,做進一步的研究。
總結
異步方法調用改善了客戶端的響應和用戶體驗,增加了服務端的可擴展性。當方法需要耗費大量的時間時,可以採用異步方式調用,提供系統併發處理的能力。對於異步方式的開發,我們可以有如上所述的廣泛選擇。
示例程序界面:
下載地址:
http://files.cnblogs.com/Terrylee/AsyncDemo.rar
出處:http://terrylee.cnblogs.com
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則視爲侵權。