深入学习Web Service系列之异步开发模式

深入学习Web Service系列之异步开发模式<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

——《深入学习Web Service系列》之一

Terrylee2005124

概述

在本篇随笔中,通过一些简单的示例来说一下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方法调用结束返回后,该线程才会被重新唤起。

示例代码:

 1 ExpandedBlockStart.gif ContractedBlock.gif /**/ /// <summary>
 2InBlock.gif/// 利用同步对象实现异步调用
 3InBlock.gif/// </summary>
 4InBlock.gif/// <param name="sender"></param>
 5ExpandedBlockEnd.gif/// <param name="e"></param>

 6 None.gif private   void  btn_AsyncClient_Click( object  sender, System.EventArgs e)
 7 ExpandedBlockStart.gifContractedBlock.gif dot.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方法一旦完成,这些工作就会被自动执行。

示例代码:

 1 ExpandedBlockStart.gif ContractedBlock.gif /**/ /// <summary>
 2InBlock.gif/// 显示结果
 3InBlock.gif/// </summary>
 4InBlock.gif/// <param name="sender"></param>
 5ExpandedBlockEnd.gif/// <param name="e"></param>

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

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

17 None.gif
18 ExpandedBlockStart.gifContractedBlock.gif /**/ /// <summary>
19InBlock.gif/// 用回调机制实现异步调用
20InBlock.gif/// </summary>
21InBlock.gif/// <param name="sender"></param>
22ExpandedBlockEnd.gif/// <param name="e"></param>

23 None.gif private   void  btn_CallBack_Click( object  sender, System.EventArgs e)
24 ExpandedBlockStart.gifContractedBlock.gif dot.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 )]

示例代码:

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

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

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

16 None.gif private   void  btn_onewayGet_Click( object  sender, System.EventArgs e)
17 ExpandedBlockStart.gifContractedBlock.gif dot.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发送通知和回应。

示例代码:

 客户端:

 1 ExpandedBlockStart.gif ContractedBlock.gif /**/ /// <summary>
 2InBlock.gif    /// 自定义的消息接收类
 3ExpandedBlockEnd.gif    /// </summary>

 4 None.gif      public   class  MyReceiver: SoapReceiver
 5 ExpandedBlockStart.gifContractedBlock.gif     dot.gif {
 6InBlock.gif        public static Form1 form;
 7InBlock.gif        private string strBody;
 8InBlock.gif
 9InBlock.gif        protected override void Receive(SoapEnvelope envelope)
10ExpandedSubBlockStart.gifContractedSubBlock.gif        dot.gif{
11InBlock.gif            strBody = envelope.InnerText;
12InBlock.gif
13ExpandedSubBlockStart.gifContractedSubBlock.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.gifContractedSubBlock.gif        dot.gif{
19InBlock.gif            form.rtb_Result.Text = strBody;
20ExpandedSubBlockEnd.gif        }

21ExpandedBlockEnd.gif    }

1 ExpandedBlockStart.gif ContractedBlock.gif /**/ /// <summary>
2InBlock.gif/// 用WSE实现异步调用
3InBlock.gif/// </summary>
4InBlock.gif/// <param name="sender"></param>
5ExpandedBlockEnd.gif/// <param name="e"></param>

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

Web Service端:

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

 7ExpandedBlockEnd.gif        }

 8 None.gif
 9 None.gif        [WebMethod]
10 None.gif         public   void  AddListener( string  listener)
11 ExpandedBlockStart.gifContractedBlock.gif         dot.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        }

22 None.gif
23 None.gif        [WebMethod]
24 None.gif         public   void  FireEvent()
25 ExpandedBlockStart.gifContractedBlock.gif         dot.gif {
26InBlock.gif            int i;
27InBlock.gif
28InBlock.gif            for(i = 0;i < this.Listeners.Count;i++)
29ExpandedSubBlockStart.gifContractedSubBlock.gif            dot.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

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值