主题
企业架构
使用异步 Servlet 处理挂起线程
页面: 1, 2, 3
WebLogic Server 6.1中已经有了未来servlet的身影,不过此功能一直都是“隐藏”在WebLogic Server中的。到WebLogic Server 9.1发行时,BEA才最终公开了这一模型。因此,我们将了解一下这个未来servlet模型究竟有何帮助。要使用该servlet,需要实现 weblogic.servlet.FutureResponseServlet 类。
我们不再需要实现doRequest()、doResponse()和doTimeout()方法,惟一要做的就是定义一个处理servlet请求的服务:
public void
service(HttpServletRequest req, FutureServletResponse rsp)
如果决定提供即时应答,只需要对FutureServletResponse实例调用send方法。但是,如果希望在以后延缓响应,则需要提供一个 TimerTask 类的扩展。下面给出了示例代码:
Timer timer = new Timer();
ScheduledTask mt = new ScheduledTask(rsp, timer);
myTimer.schedule(mt, 100);
......
public class ScheduledTask extends TimerTask {
private FutureServletResponse rsp;
Timer timer;
ScheduledTask(FutureServletResponse rsp, Timer timer){
this.rsp = rsp;
this.timer = timer;
}
public void run() {
try {
PrintWriter out = rsp.getWriter();
out.println("This is a delayed answer");
rsp.send();
timer.cancel();
} catch(IOException e) {
e.printStackTrace();
}
}
但是,如何才能在实际用例中利用这个类呢?我们再次回到Ajax范型。当新请求传入时,如果遵循传统模型,则应该向客户机提供一个即时响应并继续等待新请求。但是此处就是关键:如果将客户机请求存入缓冲区,并立即回复客户机,那么将在不同的线程中异步处理请求,且仅当后台进程终止时才发送响应。
换句话说,我们不再需要客户机-服务器轮询,而只处理服务器请求并在处理终止时通知客户机。解除请求与响应之间的耦合可以防止执行过于频繁,从而使应用程序更易于伸缩。还可以用于避免被Ajax请求淹没
但是,客户如何才能知道服务器-客户机何时终止呢?当我们处理事件驱动型编程时,需要使用观察者模式(Observer Pattern),如图4所示。
如果对于此模式还不太熟悉,我们给出了其简短描述。该模式需要依赖三个角色:Subject、Observer和Concrete Observer。
图4. 观察者模式
以下是该方法的应用示例:
public class FutureServlet extends FutureResponseServlet {
long time = 100;
public void service(HttpServletRequest req, FutureServletResponse rsp)
throws IOException, ServletException {
PrintWriter pw=null;
try {
pw = rsp.getWriter();
} catch (IOException e) {
e.printStackTrace();
}
pw.println("Request arrived");
pw.flush();
new Subject(req,rsp);
}
}
class Subject extends Observable {
private static Stack <FutureServletResponse>
stackResponses = new Stack <FutureServletResponse>();
public Subject (HttpServletRequest req, FutureServletResponse rsp){
PrintWriter pw=null;
try {
pw = rsp.getWriter();
} catch (IOException e) {
e.printStackTrace();
}
pw.println("Request sent to server");
pw.flush();
addObserver(new ConcreteObserver());
stackResponses.push(rsp);
startListening();
}
public void startListening() {
Thread t = new Thread() {
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (stackResponses.isEmpty()) {
continue;
}
FutureServletResponse rsp = stackResponses.pop();
setChanged();
// Notify event to Observer attached
notifyObservers(rsp);
}
}
};
t.start();
}
class ConcreteObserver implements Observer {
public ConcreteObserver() {}
public void update(Observable o, Object arg) {
FutureServletResponse rsp = (FutureServletResponse) arg;
PrintWriter pw=null;
try {
pw = rsp.getWriter();
pw.println("Response sent at :" + new Date() + "
");
pw.flush();
rsp.send();
pw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
如您所见,当接收到新请求时,请求和响应将存储在一个栈中。然后,Subject类将注册为ConcreteObserver类的Observer,用于保存需要在发生通知时执行业务函数。
通过这种简单而有效的方法,我们可以使Ajax应用程序更具伸缩性,从而降低执行线程的影响。该线程现在已经可以同时处理较多的请求了。
据BEA称,“此模型可为响应的处理方式提供全面的控制,并且可在线程处理方面提供更多的控制。但是,使用该类来避免挂起线程需要开发人员自己提供大部分代码。”他们建议在大多数情况下使用Abstract Asynchronous Servlet。
在理想的测试条件下,并不会出现网络超时并且处理超时的工作几乎可以忽略不计。但是,当到达服务的请求开始增加时,设计较差的Web应用程序可能会增加服务的负担并最终造成网络客户无限制阻塞。
考虑到这些原因,防止挂起线程的扩散是非常有必要的。Abstract Asynchronous Servlet可以解除响应与传入请求和超时无响应请求之间的耦合。
但是,如果需要将响应调整到未来的特定时间,并且需要全面控制线程处理,那么可以采用Future Response Servlet接口。
Francesco Marchioni 于1997年加入Java社区,是一名经过认证的Sun企业架构师。他是Pride SpA的雇员,为BEA客户设计和开发了许多在Weblogic Platform上使用的J2EE应用程序。