服务器端异步 Web 方法
Matt Powell
Microsoft Corporation
2002年10月2日
摘要:Matt Powell 介绍了如何在服务器端使用异步 Web 方法,来创建高性能的 Microsoft ASP.NET Web 服务。
简介
在九月份的第三篇专栏(英文)中,我谈到了利用 Microsoft® .NET Framework 的客户端功能通过 HTTP 异步调用 Web 服务的问题。这种调用 Web 服务的方法非常有用,使用时不必锁定您的应用程序或产生过多后台线程。现在我们了解一下在服务器端提供类似功能的异步 Web 方法。异步 Web 方法在编写 ISAPI 扩展方面具有与 HSE_STATUS_PENDING 方法类似的高性能,但不需要为管理自己的线程池编写代码,同时又具有以托管代码方式运行的所有优点。
首先我们考虑一下常规的同步 Microsoft® ASP.NET Web 方法。当您从同步 Web 方法返回时,将发送对该方法的响应。如果需要较长的时间来完成请求,则处理请求的线程会一直被占用,直到方法调用结束。不幸的是,多数较长的调用是由较长的数据库查询或对另一个 Web 服务的调用等事件引起的。例如,如果您调用数据库,当前线程会一直等待调用完成。线程无事可做,只是等待,直至听到查询的返回。当线程等待完成对 TCP 套接字或后端 Web 服务的调用时,也会出现类似的问题。
让线程处于等待状态很不好,特别是在服务器的运行压力很大的情况下。等待中的线程不会进行任何有效工作,例如为其他请求提供服务。我们需要找到一种方法,能够在服务器上开始较长的后台进程,同时又能将当前线程返回到 ASP.NET 进程池。然后,当较长的后台进程完成时,我们调用一个回调函数,结束对请求的处理,并通过某种方式通知 ASP.NET 请求已完成。实际上,这种功能可由 ASP.NET 使用异步 Web 方法提供。
异步 Web 方法的工作原理
当您使用 Web 方法编写典型的 ASP.NET Web 服务时,Microsoft® Visual Studio® .Net 只是编译您的代码以创建程序集;当收到对其 Web 方法的请求时,将调用该程序集。程序集本身并不知道关于 SOAP 的任何事情。因此,当您的应用程序首次启动时,ASMX 处理程序必须反映您的程序集,以确定提供哪些 Web 方法。对于常规的同步请求,这些操作都很简单:找出哪些方法具有关联的 WebMethod 属性、基于 SOAPAction HTTP 标头来设置调用正确方法的逻辑。
对于异步请求,在反映过程中,ASMX 处理程序寻找具有某种签名并将签名识别为异步的 Web 方法。该处理程序将寻找符合以下规则的方法对:
- BeginXXX 和 EndXXX Web 方法,其中 XXX 是任意字符串,表示要提供的方法的名称。
- BeginXXX 函数返回一个 IAsyncResult 接口,并分别接受 AsyncCallback 和一个对象,作为其最后两个输入参数。
- EndXXX 函数接受一个 IAsyncResult 接口,作为其唯一的参数。
- 两个方法都必须使用 WebMethod 属性进行标识。
如果 ASMX 处理程序发现两个方法符合上述所有条件,则将方法 XXX 作为常规的 Web 方法在其 WSDL 中提供。该方法将接受在 BeginXXX 的签名中的 AsyncCallback 参数之前定义的参数作为输入,并返回由 EndXXX 函数返回的内容。因此,如果某个 Web 方法具有如下同步声明:
[WebMethod]
public string LengthyProcedure(int milliseconds) {...}
则异步声明将为:
[WebMethod]
public IAsyncResult BeginLengthyProcedure(
int milliseconds,
AsyncCallback cb,
object s) {...}
[WebMethod]
public string EndLengthyProcedure(IAsyncResult call) {...}
每个方法的 WSDL 都是相同的。
在 ASMX 处理程序反映程序集并检测到某个异步 Web 方法后,它必须以不同于处理同步请求的方式处理对该方法的请求。它将调用 BeginXXX 方法,而不是某个简单方法。它将传入的请求还原序列化到要传递到函数的参数中(与处理同步请求时一样);但是它还将指针传递到一个内部回调函数(作为 BeginXXX 方法的额外 AsyncCallback 参数)。
这种方法类似于 .NET Framework 中 Web 服务客户端应用程序的异步编程模式。如果客户端支持异步 Web 服务调用,则可以为客户端计算机释放占用的线程;如果服务器端支持异步 Web 服务调用,则可以释放服务器计算机上占用的线程。但这里有两个关键的区别。首先,不是由服务器代码调用 BeginXXX 和 EndXXX 函数,而是由 ASMX 处理程序调用。其次,您要为 BeginXXX 和 EndXXX 函数编写代码,而不能使用由 WSDL.EXE 或 Visual Studio .NET 中的 Add Web Reference(添加 Web 引用)向导生成的代码。但结果是相同的,即释放线程以使其能够执行其他进程。
ASMX 处理程序调用服务器的 BeginXXX 函数后,会将线程返回到进程线程池,使之能够处理接收到的任何其他请求。但是,还不能释放请求的 HttpContext。ASMX 处理程序将等待,直到它传递给 BeginXXX 函数的回调函数被调用,它才结束处理请求。
一旦回调函数被调用,ASMX 处理程序将调用 EndXXX 函数,使您的 Web 方法可以完成任何所要执行的处理,并且可以得到被序列化到 SOAP 响应中的返回数据。EndXXX 函数返回后将发送响应,只有此时该请求的 HttpContext 才得到释放。