| 目前,基于Internet(或Intranet)的企业应用需求日益增多,以Web Services为代表的应用程序间的远程通信需求日渐频繁。在这种需求下,开发人员的工作正越来有越多地集中在以C/S(Client/Server,客户机/服务器模式)和B/S(Browser/Server,浏览器/服务器模式)开发模式实现的应用领域。而这两种开发模式中,客户端的开发至关重要。
一般地,远程调用从客户端发起,根据其调用方式的不同,可以将客户端的应用程序调用分成两种:异步化和同步化调用(其实服务端也可做同样的区分,只不过服务端不需要从代码上显式地提供这种支持)。
如果你在开发客户端应用程序的过程中没有刻意去对应哪种调用模式,你极有可能是在做同步化处理。这一方面是因为同步化编程相对简单、易于实现,还有可能就是你的应用程序任务单一或者需要顺序执行(后面的执行必须等待前面代码所产生的数据),另外也许就是你的客户对应用程序的伸缩性要求不高(有足够的耐心等待程序的迟钝反应)。
的确,对开发人员来说,同步化处理是很容易实现的:应用程序通过建立一个线程启动一个任务,这个任务完成后,其他的事情得以继续。
然而,实际中我们面临的应用任务相当复杂,仅仅需要提供单一任务的应用程序不多了。更多的时候我们不仅需要同时执行多个任务,而且还需要经由Internet与远程进程通信。这时,同步化处理的局限性就显现出来了。你可能无法容忍一个应用程序在某个时候停下来等待一个任务的完成后再继续;同样地,不可预知的客户数量需要远程方无差异地做出反应;他们需要知道所发出的信号是否成功送达?远程处理需要多长时间?如何获得返回的数据(如果有的话)?很明显,这种情况下,同步化处理难于实现。
事实上,实现多线程机制的同步化处理可以部分满足以上需求。当我们的任务不只一个时,我们可以创建同样数目的线程执行对应的任务(要说明的是,多线程仍然是同步化处理的一种方式,只不过处理器时间片很短,线程间的切换很快,感觉就象多个任务在同时执行一样)。然而,众所周知,多线程的处理不是件容易的事情,一方面,创建多个线程极大消耗系统资源,另一方面,多个线程的管理控制也相当困难,更重要的是,对线程间共享资源的合理处理以及完全避免死锁需要程序员付出艰辛的工作而且相当危险。
其实,异步化处理能很好地完成以上任务。而且,.Net CLR(Common Language Runtime,通用语言运行时)对异步化处理提供了丰富的支持,使得异步化编程易于实现。
一、.Net CLR提供的异步化支持
一直以来,异步化处理在许多应用领域优势明显,前景相当诱人。然而,在.net framework之前,实现异步化的模式设计及代码编写却一直令人畏惧,那个时候,异步化处理实际上需要跟多线程相结合,这无疑增加了复杂度,使得许多开发人员望而却步。
幸运的是,.Net Framework为异步化处理提供了丰富的支持。它不仅在许多类对象和方法中内置了异步化代码,大大减少了代码编写的复杂度,同时还涉及了许多应用领域,包括文件等I/O操作,网络传输、消息传递、asp.net web表单和web服务等。这些支持使得异步化编程成为了 .NET 框架中的核心概念之一。
.Net Framework中对异步化处理的支持,得益于.Net的委托机制。委托是.Net中异步化处理得以实现的一个重要概念。由于执行异步化处理的调用方需要知道其所发出的操作何时完成(有时也需要知道返回的信息),一种重要的实现方法就是使用回调原理,而回调在.Net中通过委托来实现。
委托是.Net Framework集成的一个重要概念。它类似传统C/C++下的函数指针,用于在.Net中提供回调功能。与函数指针不同的是,委托受到.Net CLR (通用语言运行时)的支持,因而是类型安全的自描述单元,从而避免了C/C++中函数指针容易产生的地址混乱、调用困难等潜在隐患。
.Net委托类作为容器起作用。委托包含关于单个方法的信息,用作异步化处理时,委托被用来在服务器对象和客户机应用程序之间提供通信机制。
当在代码中创建一个委托时,.Net编译器除了创建一个称为Invoke的方法外,还创建了一个类对象,用来创建异步化连接实例对象,该实例对象用于实现与委托引用的方法的异步化连接,该类对象也包含名为BeginInvoke和EndInvoke的两个方法,用于在客户端启动远程异步调用并从服务端返回结果。
二、Web Services中的异步化处理模式
作为.Net下Asp.net应用程序的一种,Web Services是以独立于平台的方式、通过标准的Web协议、可以由程序模块访问的应用程序逻辑单元,Web Services通信的基础主要是基于Internet、构建于Http之上的SOAP(Simple Object Access Protocol,简单对象访问协议),这使得Web Services能够为远程过程调用及进程间通信提供相对简单的实现。
在Visual Studio.Net IDE中,编写服务端Web Service代码很简单,同样地,在Visual Studio.Net IDE中编写消费Web Service的客户端代码也很容易。单从代码上看,执行消费Web服务的方法调用与本机代码中的函数调用没什么不同。但实际上,两者在实现机制上却有很大的区别。这是因为函数调用多在同一进程内部发生,虽然同一进程中可包括多个应用程序域,但不同的应用程序域之间交互并不困难,因而进程内函数的调用相对简单。而在消费Web服务时,函数调用被捆绑在SOAP消息中,并且通过Internet经由Http协议汇集在一起,程序需要跨不同进程边界通信,这时,Web服务的性能会因不同的调用方法而发生非常大的变化。开发这类客户机应用程序时,相比同步化处理而言,异步化处理将大大提高应用程序的可用性、伸缩性及交互性,因而成为首选的开发模式。
异步消费Web Service可图示如下:
图中,带圈数字序号表示应用程序的数据流程。它们用于表示:
◇1、客户方通过代理类调用Web服务方法,信号经由Internet,发送给远程服务器。
◇2、调用立即返回,客户端的当前线程不阻塞且不等待结果,代码按顺序继续执行,这是与同步化处理的不同的地方;
◇3、远程服务器完成Web Service方法处理后,将信息返回客户机调用处。
从数据流动方向上就能知道异步化处理的优越性,更重要的是实现以这种方式消费Web Service并不困难,因为针对Web Service的异步化处理同样遵循.Net中关于异步化处理的通用模式:
· 客户端应用程序决定消费Web Service的调用是以异步化处理还是同步化处理;
· 服务器自动支持客户端的异步行为,也就是说,服务器端不需要额外为此编写代码,.Net CLR能有效识别并管理客户端与服务器视图之间的差异。
· 服务器也可以选择显式支持异步化处理,这样做在某种情况下可以使客户端的异步行为更有效。
三、异步消费Web Service时的几种交互方法
在Visual Studio.Net IDE中开发客户端消费Web服务代码时,首先要添加"Web引用"。而一旦向项目添加了Web引用,Visual Studio.net IDE就会透明地生成代理类。
默认时,Web引用的名称即为被引用的服务器的名字。如果引用的是本机上的服务,则Web引用的名称就是localhost。你应该将它修改为有意义的名字以增强代码的可读性,并使得通过代理对象来访问Web服务更加方便。
即便.Net Framework提供了先进的方法,在客户方高效地异步消费Web Service也不能说是极容易的事。因而很多时候,仅仅分析应用程序的需求是不够的,你还要在以下几种方法中做出抉择,这些方法都能实现Web Service的异步消费并在性能上差异很大。
1、委托及回调机制
在Visual Studio.Net IDE中,由Web引用产生的代理类支持以回调方式实现异步化处理。Web服务代理类文件位于客户端"Web 引用"所嵌套的Reference.cs文件中。(默认路径类似http://localhost/Web 服务应用程序名/Web 引用/localhost/Reference.cs,可以通过点击"显示所有文件"图标显现出该文件)。这个文件是系统自动产生的,不需要我们另行创建。
查看Web Service代理类文件代码可以发现,代理类为异步化处理提供了支持。这是通过提供BeginX方法和EndX方法(这里,X表示服务方实现的web服务方法名称,下同)来实现的。更进一步说,系统自动产生的BeginX方法和EndX方法及其他代码主要针对以回调机制实现的异步化处理中。
首先,客户方通过调用BeginX方法开始异步消费Web Service。执行BeginX方法后,系统自动在后台创建一个线程,专门用于与远程进程通信,因而客户方的当前线程并不阻塞并得以继续执行其他任务。
BeginX方法除了接受异步化处理所需要的输入参数外,在异步化处理完成时,它也可以把AsyncCallback委托送给它被调用的地方,AsyncCallback委托将作为客户机应用程序获得方法调用的结果的函数指针,唯一的要求是客户方代码在执行Web Service调用之初显式使用回调机制。
要注意,BeginX方法的返回值并不是Web服务中方法的返回值(在实现Web 服务的服务端代码中,Web服务方法或许返回一个字符串或者一个整数),而是一个类型为System.Web.Services.Protocols.WebClientAsyncResult的表示等待状态的对象,这个对象相当重要,客户机应用程序通过使用它实现IAsyncResult接口来确定异步化操作的状态,它还可以管理对同一个Web服务的多个调用。
这之后,通过调用EndX方法,并将AsyncResult对象(该对象由BeginX方法返回)作为方法的参数, 客户机应用程序就能够获得Web Service异步化处理的结果数据,这个结果数据也就是实际的Web服务方法的返回值(如果有返回的话)。
不过,如果EndX方法被较早地调用,它将会阻塞调用线程直到异步化执行方法返回,然后才能把结果返回给你。所以,对该方法的调用要掌握好时间,尽量通过调用EndX方法较早地完成操作,以减少系统等待。
使用回调机制实现异步化处理是功能最强的方式。它允许在Web服务被调用、执行和返回信息期间继续完成它所需要的工作。这时,客户方代码通过在BeginX方法的参数中显式地确定把地址传递到一个Public方法(该方法由委托机制实现),以便在异步化调用完成后可以把结果返回到程序中。
2、状态检测机制
通常,异步消费Web Service还有另外的方式,其中一种通过定期地检查从BeginX方法返回的IAsyncResult对象的IsCompleted属性值来等待异步化调用的完成,:
IAsyncResult ar=AppSettings.BeginGetAppSettings(key,delay,callback,asyncstate);
While(ar.IsCompleted==false)
{
//code here(在调用从远程服务方返回期间,客户端要执行的其他任务代码)
}
|
这可以说是最为简单的一种方式,当然局限性也很明显:为返回IasyncResult.IsCompleted属性(该属性指示远程web服务的执行情况)而进行不断的检查势必增加系统开销。
3、WaitHandle系列方法
另外,IAsyncResult类也提供了一系列继承自WaitHandle(WaitOne,WaitAll等)的方法来支持用户管理各种对Web服务的调用,并提供了一种方式来等待异步化调用的完成,代码片段大致如下:
IAsyncResult ar=WebProxy.BeginX(para1,para2,null,null);//开始异步化调用
…
ar.AsyncWaitHandle.WaitOne();//阻塞调用,以等待web服务返回结果
…
result=WebProxy.EndX(ar);//取得web服务返回值
…
|
这里,WebProxy表示web引用的代理类,para1,2表示参数,result表示返回值。不过,在调用继承自WaitHandle的系列方法时,调用方将被无限期阻塞,直到当前实例收到异步化操作完成时生成的信号。
4、调用EndX方法等待结果
客户方应用程序甚至可以只调用EndX方法来获得Web Service方法的执行结果。该方法实际将创建一个同步化调用条件,这时,当前的调用线程将被阻塞,一直到Web服务完成,调用方才能继续处理其他任务。
四、异步消费Web Service的场合及应用示例
一般地,异步消费Web Service由远程客户机应用程序发起调用,通过网络进行数据传输。应用程序的种类极多,可能是一个基于Internet的Asp.net页面,一个基于桌面的Windows Form应用程序,也可能是另一个Web服务,还可能是其他支持远程调用的应用程序组件等。对网络的两端(调用方和服务方)来说,另一端的情况无法预见。
然而,无论怎样,我们需要提高消费Web Service的客户应用程序的可伸缩性,而异步化处理能够提供这种伸缩性。具体地,在消费Web 服务时,以下情况需要考虑异步化处理。
如果Web服务需要一定的时间来完成,而这个时间并不在用户方可接受的时间范围之内,需要考虑异步消费。
如果客户机应用程序需要同时对多个远程Web服务进行调用,在这种情况下,应该使用异步化远程过程调用而不是创建多个线程。
如果客户方应用程序需要在界面上进行适时交互,这时,应该实现异步消费Web Service以避免界面停止反应,这至少让人感觉友好。
如果基于Internet的远程通信需要考虑网络带宽约束、系统资源约束及响应速度等问题时,消费Web服务应该考虑使用异步化处理方式。
适合异步消费Web Service 的场合还有很多。
异步消费Web Service通常会在客户端获得明显的性能改进,而提供Web Service的服务器方对此并不知情,它完全不知道进行的是同步化调用还是异步化调用,这样,开发人员就不必专门设计处理不同类型的调用的Web Services。
合理地异步消费Web Service可能改善系统的使用,并且在等待Web服务的结果时可以避免客户机端的延时。实际上,随着Web Service的广泛应用和网络传输速度及带宽的提高,异步消费Web Service将更加普及。
关于异步消费Web Service的示例,MSDN技术资料上有很多,小巧而经典,也很容易懂,建议去分析研究,这里就不另外提供了。
参考资料:《C# Web服务高级编程》、《Asp.net Web服务高级编程》、MSDN技术资料
|