主题
企业架构
作者:Francesco Marchioni
12/04/2007
BEA WebLogic Server 9.2及以上版本将公开一个Abstract Asynchronous Servlet类,可用于解除接收servlet请求与发送其响应之间的耦合。该类还提供了一个Future Response Servlet,用于支持服务器使用一个不同的线程(而不是处理传入请求的线程)来处理servlet响应。传统servlet模型的这两个扩展都可以避免挂起线程并将长时间运行的作业与servlet范例集成在一起。本文将介绍这两个特性,同时将提供一些示例。
是否希望通过在Web应用程序中控制响应时间来实现服务质量(QoS)?从最简单的形式来说,服务质量需求就是将响应时间控制在特定的时间(秒)内。如果无法满足这一需求,则会提供一条有意义的错误消息。
传统的servlet线程模式相当简单:应用服务器分配特定数量的线程,并提供给Web应用程序使用(参见图1)。当新请求传入并可供服务使用时,应用程序将分配一个线程。从此之后,servlet线程将一直占用内存池,直到它完成所有任务。

图1. 传统servlet线程模式
如果servlet需要运行一个长时间的任务,则会造成一些问题。 要解决这一问题,最常用的方法是使用JMS或Message Driven Bean解除Web请求与长时间运行后台进程之间的耦合。这样便可解决不需要将处理结果立即返回响应的情况。
这听上去像是一个“Fire-and-forget”场景。但是,如果需要向客户机返回一些数据,那么servlet线程极有可能会成为挂起线程。
在大多数情况下,挂起线程的意思就是在联系后台系统获取发送请求所需要的数据时受到了阻塞。其典型场景就是通过JDBC连接远程数据库。
问题的关键在于,如果所需资源速度变慢或完全不可用,则需要确保应用程序知道如何处理这一情况。大多数情况下,这并不是什么问题,因为超时套接字到远程数据库服务的代码都由数据源和JDBC驱动程序处理。但是,在需要手动编写超时策略时,该场景将变得极为复杂。
许多程序员都害怕处理网络超时。最常见的问题是,将没有超时支持的单线程网络客户机扩展为复杂的多线程时,每个单独的线程都需要测试网络超时,并且阻塞线程与主应用程序之间需要某种形式的通知流程。
本文将介绍如何使用BEA WebLogic Server的未来响应模型(Future Response Model)来编写能够有效处理超时的Java Web应用程序。这一过程非常简单。通过解除响应与传入请求及超时无响应请求之间的耦合,该模型还可以防止挂起线程。为避免这种线程挂起场景,WebLogic Server提供了两个类专门用于异步处理HTTP请求,其原理是解除响应与处理传入请求的线程之间的耦合。以下部分将详细介绍这两个类。
AbstractAsyncServlet类的实现将解除接收servlet请求与发送响应之间的耦合。我们实现Abstract Asynchronous Servlet类的方法是扩展 weblogic.servlet.http.AbstractAsyncServlet 类。以下是需要实现的方法:
public boolean doRequest(RequestResponseKey rrk): 该方法是联系servlet的初始点。它接受的输入参数为RequestResponseKey类,RequestResponseKey类是传统servlet请求的包装器。下文将会介绍,我们可以使用请求判断servlet是否响应。public void doResponse (RequestResponseKey rrk, Object context): 该方法将处理servlet响应。正如本文所述,初始线程并不需要处理此方法。public void doTimeout (RequestResponseKey rrk): 如果未在特定时间段内发送servlet响应,则服务器将会触发doTimeout()方法。Servlet类还提供了一个静态方法:
static void notify(RequestResponseKey rrk, Object context): 调用此方法将通知服务器应该向键rrk发送一个响应。只实现抽象类还不足以实现解耦。解除请求与响应之间的耦合可以通过doRequest()方法中的 TimerListener 类来实现。可以使用工厂模式获得TimerListener类的一个新实例(实际上是通过TimerManagerFactory)。TimerListener线程可以针对AbstractAsyncServlet实例调用notify()方法,从而最终触发响应的发送。以下示例将演示如何创建这种计时器:
TimerManagerFactory.getTimerManagerFactory().
getDefaultTimerManager().schedule(new TimerListener() {
public void timerExpired(weblogic.timers.Timer arg0) {
try {
// This will trigger delivery of the response.
AbstractAsyncServlet.
notify(rrk, null);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}, 1000);
此处的关键在于,如果该类位于doRequest()方法中,则需要等到计时器到期并调用notify()方法时才会发送响应。反过来,notify()将调用doResponse()方法。如果未在指定时间内调用notify()方法,则会调用doTimeout()方法。
图2展示了客户机与服务器之间的交互流程图。

图2.使用AbstractAsyncServlet实现请求/响应解耦
我们来看看该模式的典型使用。其中一个场景为,服务质量应用程序需要在特定时间内向客户机发送一个响应消息(正如前面如述)。但是,即便是不同的用例(如延时的请求处理)也可以为我们带来好处:试想某个应用程序需要访问远程资源(比如说远程DB链接)或CPU密集算法。在此类场景中,延时处理请求(固定时间量)可以帮助防止过多同时访问。到目前为此,惟一的选择便是等待使用有价值的线程。
下面将演示该方法的应用示例。