深入學習Web Service系列之異步開發模式(轉載)

 

概述<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

在本篇隨筆中,通過一些簡單的示例來說一下Web Service中的異步調用模式。調用Web Service方法有兩種方式,同步調用和異步調用。同步調用是程序繼續執行前等候調用的完成,而異步調用在後臺繼續時,程序也繼續執行,不必等待方法處理完成而直接返回。具體的調用流程見下圖:

 PIC017.jpg

對於同步調用方法而言,UI線程依賴於方法的實現,方法執行時間過長將導致UI無法及時與用戶進行交互。我們知道,在Windows客戶端中,每個進程都有單一的UI進程,在服務器中,可擴展性依賴於線程的使用。對於異步調用方法而言,能夠及時於用戶交互響應,從而提供了良好的用戶體驗;同時也可以改善服務器的可擴展性,將服務器與通訊問題隔離。

客戶端異步調用方法

在客戶端異步調用是完全基於Proxy的方法,異步行爲最簡單的模式。Visual StudioWSDL.EXE提供對它的直接支持。所以我們不必在Web服務應用程序中編寫額外的代碼來處理異步調用。

遍及.NET Framework的異步調用有一個基礎的設計模式:Begin方法和End方法,他們分別用於開始和終止異步處理。Visual StudioWSDL.exe生成了這兩種方法:

Begin<WebServiceMethodName>——該方法通知Web服務開始處理調用,並立即返回。該方法不返回Web服務調用所指定的數據類型,而是返回一種實現IasyncResult接口的數據類型。

End<WebServiceMethodName>——該方法通知Web服務返回先前啓動的Web方法所生成的結果。

IasyncResult接口包含了WaitHandle類型的AsyncWaitHandle特性。這個公共接口允許用戶的客戶應用程序等待調用,而且,該接口將用AnyAll語義(例如WaitHandle.WaitOneWaitAnyWaitAll)作爲信號通知客戶應用程序。例如,如果想要客戶應用程序異步等候一個Web方法,可調用WaitOne來處理要完成的Web服務。

一般來說,客戶端異步代理方法有兩種實現機制:使用同步對象和回調機制(也許你可能對用這個詞不習慣,實在找不到第二個詞來代替,暫且這樣稱呼吧)

同步對象

同步對象允許用戶對Web服務的方法進行調用(使用Begin方法),然後繼續處理。在後面的程序中,可以調用End方法,傳遞同步對象,以便得到調用結果。這種方式下,能夠繼續執行函數中的程序流程,而不執行回調處理。在這裏調用WaitOne()方法會掛起當前線程,避免忙等待的發生,直到Web Services方法調用結束返回後,該線程纔會被重新喚起。

示例代碼:

 1ExpandedBlockStart.gif/// <summary>
 2InBlock.gif/// 利用同步對象實現異步調用
 3InBlock.gif/// </summary>
 4InBlock.gif/// <param name="sender"></param>
 5ExpandedBlockEnd.gif/// <param name="e"></param>

 6None.gifprivate void btn_AsyncClient_Click(object sender, System.EventArgs e)
 7ExpandedBlockStart.gif{
 8InBlock.gif    IAsyncResult ar = wsc.BeginHello(this.txt_UserName.Text, nullnull);
 9InBlock.gif
10InBlock.gif
11InBlock.gif    MessageBox.Show("Continue to do some other things");
12InBlock.gif
13InBlock.gif    ar.AsyncWaitHandle.WaitOne();
14InBlock.gif
15InBlock.gif    strHello = wsc.EndHello(ar);
16InBlock.gif
17InBlock.gif    this.rtb_Result.Text = strHello;
18ExpandedBlockEnd.gif}

回調機制

從本質上說,異步回調機制是委託的.NET等價物,它通過在異步操作完成時建立一個被調用的單獨方法來進行工作。調用應用程序能夠繼續處理其他的任務,直到回調函數被調用爲止。這就意味着處理已經完成了,應用程序可以正常運行了。使用同步對象不同於回調機制的區別是,當檢查Web方法是否已經完成,以及檢查Web方法中是否含有需要的結果時,我們無法對其進行控制,而在回調的情況中,Web方法一旦完成,這些工作就會被自動執行。

示例代碼:

 1ExpandedBlockStart.gif/// <summary>
 2InBlock.gif/// 顯示結果
 3InBlock.gif/// </summary>
 4InBlock.gif/// <param name="sender"></param>
 5ExpandedBlockEnd.gif/// <param name="e"></param>

 6None.gifpublic void UpdateResult(object sender, EventArgs e)
 7ExpandedBlockStart.gif{
 8InBlock.gif    this.rtb_Result.Text = strHello;
 9ExpandedBlockEnd.gif}

10None.gif
11None.gifpublic void OnHelloComplete(IAsyncResult ar)
12ExpandedBlockStart.gif{
13InBlock.gif    strHello = wsc.EndHello(ar);
14InBlock.gif
15InBlock.gif    this.rtb_Result.Invoke(new EventHandler(UpdateResult));
16ExpandedBlockEnd.gif}

17None.gif
18ExpandedBlockStart.gif/// <summary>
19InBlock.gif/// 用回調機制實現異步調用
20InBlock.gif/// </summary>
21InBlock.gif/// <param name="sender"></param>
22ExpandedBlockEnd.gif/// <param name="e"></param>

23None.gifprivate void btn_CallBack_Click(object sender, System.EventArgs e)
24ExpandedBlockStart.gif{
25InBlock.gif    AsyncCallback cb = new AsyncCallback(OnHelloComplete);
26InBlock.gif
27InBlock.gif    wsc.BeginHello(this.txt_UserName.Text, cb, null);
28ExpandedBlockEnd.gif}

使用回調機制還是同步對象取決於用戶所面臨的具體情況。在檢查異步調用是否完成時,如果願意對處理過程進行控制,那麼可以選擇使用同步對象。如果覺得自己編寫代碼來完成對Web服務的調用,且當方法一旦執行完畢就立即由所調用的特殊函數來處理所返回的結果更適合一些,那麼就更適合用回調機制。

在客戶端使用異步方法調用,可以改進UI響應度,在服務器端不需要實現異步操作,對服務器來說是透明的,而且客戶端能夠在任何時間選擇阻塞。

服務端使用Soap One-Way方法

在服務器端使用One-Way方法實現異步調用,其實質是將單項消息發送到端點。這種方式的特點是方法沒有返回值,客戶端方法不會從調用的服務器端方法中收到返回值;我們無法判斷方法結束的時間,對於結果需要顯式通知或者輪詢。

Web 服務端,我們使用[SoapDocumentMethod]定義One-Way方法:

None.gif [System.Web.Services.Protocols.SoapDocumentMethod(OneWay=true)]

示例代碼:

 1ExpandedBlockStart.gif/// <summary>
 2InBlock.gif/// One-Way方式的異步調用Set
 3InBlock.gif/// </summary>
 4InBlock.gif/// <param name="sender"></param>
 5ExpandedBlockEnd.gif/// <param name="e"></param>

 6None.gifprivate void btn_OneWay_Click(object sender, System.EventArgs e)
 7ExpandedBlockStart.gif{
 8InBlock.gif    wsc.SetHello(this.txt_UserName.Text);    
 9ExpandedBlockEnd.gif}

10None.gif
11ExpandedBlockStart.gif/// <summary>
12InBlock.gif/// One-Way方式的異步調用Get
13InBlock.gif/// </summary>
14InBlock.gif/// <param name="sender"></param>
15ExpandedBlockEnd.gif/// <param name="e"></param>

16None.gifprivate void btn_onewayGet_Click(object sender, System.EventArgs e)
17ExpandedBlockStart.gif{
18InBlock.gif    strHello = wsc.GetHello();
19InBlock.gif
20InBlock.gif    this.rtb_Result.Text = strHello;
21ExpandedBlockEnd.gif}

One-Way方法不適合於下列情況:

l         方法需要對結果輪詢

l         方法需要同步

服務端使用WSE SoapSenderSoapRecevier

在進行本部分內容之前,我們需要安裝WSE2.0WSE支持面向消息的編程,爲我們提供了SoapSenderSoapReceiver基類,它能夠支持發送和接收SoapEnvelopes,同時它也通過SoapClientSoapService提供了更多的事務支持。SoapSenderSoapReceiver在客戶端和服務端同時實現,客戶端使用SoapSender發送消息,同時可選擇使用SoapReceiver接收消息;服務端使用SoapReceiver接收消息,同時也可以選擇使用SoapSender發送通知和迴應。

示例代碼:

 客戶端:

 1ExpandedBlockStart.gif/// <summary>
 2InBlock.gif    /// 自定義的消息接收類
 3ExpandedBlockEnd.gif    /// </summary>

 4None.gif    public class MyReceiver: SoapReceiver
 5ExpandedBlockStart.gif    {
 6InBlock.gif        public static Form1 form;
 7InBlock.gif        private string strBody;
 8InBlock.gif
 9InBlock.gif        protected override void Receive(SoapEnvelope envelope)
10ExpandedSubBlockStart.gif        {
11InBlock.gif            strBody = envelope.InnerText;
12InBlock.gif
13ExpandedSubBlockStart.gif            ///注意:在進行此項之前,一定要把rtb_Result控件的屬性設爲Public
14InBlock.gif            form.rtb_Result.Invoke(new EventHandler(UpdateBody));
15ExpandedSubBlockEnd.gif        }

16InBlock.gif
17InBlock.gif        void UpdateBody(object sender, System.EventArgs e)
18ExpandedSubBlockStart.gif        {
19InBlock.gif            form.rtb_Result.Text = strBody;
20ExpandedSubBlockEnd.gif        }

21ExpandedBlockEnd.gif    }

1ExpandedBlockStart.gif/// <summary>
2InBlock.gif/// 用WSE實現異步調用
3InBlock.gif/// </summary>
4InBlock.gif/// <param name="sender"></param>
5ExpandedBlockEnd.gif/// <param name="e"></param>

6None.gifprivate void button4_Click(object sender, System.EventArgs e)
7ExpandedBlockStart.gif{
8InBlock.gif    wsc.FireEvent();
9ExpandedBlockEnd.gif}

Web Service端:

 1None.gifprivate ArrayList Listeners
 2ExpandedBlockStart.gif        {
 3InBlock.gif            get
 4ExpandedSubBlockStart.gif            {
 5InBlock.gif                return (ArrayList)Application["Listeners"];
 6ExpandedSubBlockEnd.gif            }

 7ExpandedBlockEnd.gif        }

 8None.gif
 9None.gif        [WebMethod]
10None.gif        public void AddListener(string listener)
11ExpandedBlockStart.gif        {
12InBlock.gif            ArrayList alist = (ArrayList)Application["Listeners"];
13InBlock.gif
14InBlock.gif            if(alist == null)
15InBlock.gif                alist = new ArrayList();
16InBlock.gif
17InBlock.gif            alist.Add(listener);
18InBlock.gif
19InBlock.gif            Application["Listeners"= alist;
20InBlock.gif
21ExpandedBlockEnd.gif        }

22None.gif
23None.gif        [WebMethod]
24None.gif        public void FireEvent()
25ExpandedBlockStart.gif        {
26InBlock.gif            int i;
27InBlock.gif
28InBlock.gif            for(i = 0;i < this.Listeners.Count;i++)
29ExpandedSubBlockStart.gif            {
30InBlock.gif                SoapEnvelope envelope = new SoapEnvelope();
31InBlock.gif
32InBlock.gif                envelope.SetBodyObject("Hello World!");
33InBlock.gif
34InBlock.gif                envelope.Context.Addressing.Action = new Action((string)(this.Listeners[i]));
35InBlock.gif
36InBlock.gif                envelope.Context.Addressing.ReplyTo = new ReplyTo(new System.Uri((string)(this.Listeners[i])));
37InBlock.gif
38InBlock.gif                SoapSender peerProxy = new SoapSender(new System.Uri((string)(this.Listeners[i])));
39InBlock.gif
40InBlock.gif                peerProxy.Send(envelope);
41ExpandedSubBlockEnd.gif            }

42ExpandedBlockEnd.gif        }

服務端使用WSE 自定義SoapMSMQ傳輸

SoapMSMQ是一款開源軟件,簡化使用WSE進行MSMQ操作,下載地址:

http://www.codeproject.com/useritems/SoapMSMQ.asp

SoapMSMQ完全支持事務,具有如下特點:

l         在事務中,請求要被同步初始化

l         同步階段排隊請求,並且返回令牌

l         異步階段處理各個事務

l         所有持有令牌的請求都保證會被處理,但可能會不成功

l         支持向客戶端發送通知

SoapMSMQ感興趣的朋友可以下載下來後,做進一步的研究。

總結

異步方法調用改善了客戶端的響應和用戶體驗,增加了服務端的可擴展性。當方法需要耗費大量的時間時,可以採用異步方式調用,提供系統併發處理的能力。對於異步方式的開發,我們可以有如上所述的廣泛選擇。

示例程序界面:
PIC018.jpg
下載地址:

http://files.cnblogs.com/Terrylee/AsyncDemo.rar

 

作者:TerryLee
出處:http://terrylee.cnblogs.com
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則視爲侵權。
發佈了4 篇原創文章 · 獲贊 0 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章