深入学习Web Service系列之
异步开发模式
——《深入学习
Web Service
系列》之一
Terrylee
,
2005
年
12
月
4
日
概述
在本篇随笔中,通过一些简单的示例来说一下
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
方法调用结束返回后,该线程才会被重新唤起。
示例代码:
1
/**/
///
<summary>
2
///
利用同步对象实现异步调用
3
///
</summary>
4
///
<param name="sender"></param>
5
///
<param name="e"></param>
6
private
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
方法一旦完成,这些工作就会被自动执行。
示例代码:
1
/**/
///
<summary>
2
///
显示结果
3
///
</summary>
4
///
<param name="sender"></param>
5
///
<param name="e"></param>
6
public
void
UpdateResult(
object
sender, EventArgs e)
7
{
8
this
.rtb_Result.Text
=
strHello;
9
}
10
11
public
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>
23
private
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
方法:
[System.Web.Services.Protocols.SoapDocumentMethod(OneWay
=
true
)]
示例代码:
1
/**/
///
<summary>
2
///
One-Way方式的异步调用Set
3
///
</summary>
4
///
<param name="sender"></param>
5
///
<param name="e"></param>
6
private
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>
16
private
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
发送通知和回应。
示例代码:
客户端:
1
/**/
///
<summary>
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
}
1
/**/
///
<summary>
2
///
用WSE实现异步调用
3
///
</summary>
4
///
<param name="sender"></param>
5
///
<param name="e"></param>
6
private
void
button4_Click(
object
sender, System.EventArgs e)
7
{
8
wsc.FireEvent();
9
}
Web Service端:
1
private
ArrayList Listeners
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://www.cnblogs.com/Files/Terrylee/AsyncDemo.rar
原文地址:http://terrylee.cnblogs.com/archive/2005/12/05/290845.html