主题
企业架构
使用异步 Servlet 处理挂起线程
页面: 1, 2, 3
假设,某个应用程序的作用是输出股票交易报价。我们将使用JCA接口获取股票价值,而该接口用于访问某个银行系统。用户需要在几秒钟之内获取反馈。但是,如果数千名用户在同一时间段时连接系统,则响应极有可能会延时,并且最坏的情况将导致HTTP连接超时。
不过所造成的损害远不止于此:如果用户发现该站点无法响应,则会单击刷新按钮,大量的新请求将会淹没应用服务器。
需要采取相同的措施来制止这一问题。如果后台资源受到阻塞,则无法满足用户的请求。但是,如果在收集最新的股票代码时就将其存入缓存,那么就可以在超时回调之前生成此信息,同时指出数据采集的时间。在本例中,我们将超时设置为2500毫秒。如果在此时间内未生成响应,则会调用doTimeout()方法。图3显示了其体系结构。
图3. 未及时通知servlet时将会触发doTimeout()回调
此servlet的详细代码如下所示:
public class AsyncServlet extends AbstractAsyncServlet {
// Need JDK 1.5
Hashtable <String,Long> hashStocks = new Hashtable<String,Long>();
Date dateLastUpdate = new Date();
public void init() {
// The default request timeout period in milliseconds.
super.setTimeout(2500);
// The default interval period in milliseconds at which
// requests will be checked for timeouts.
super.setScavangeInterval(1);
// Populate hashtable with Stock initial value
hashStocks = readStocksValue();
}
public boolean doRequest(final RequestResponseKey rrk)
throws ServletException, IOException {
HttpServletRequest req = rrk.getRequest();
HttpServletResponse res = rrk.getResponse();
// Read Stock quotation from Back end
hashStocks = readStocksValue();
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);
return true;
}
public void doResponse(RequestResponseKey rrk, Object context)
throws ServletException, IOException {
HttpServletRequest req = rrk.getRequest();
HttpServletResponse res = rrk.getResponse();
// Display fresh quotations
displayStockQuotations(res);
}
public void doTimeout(RequestResponseKey rrk) throws ServletException,
IOException {
HttpServletRequest req = rrk.getRequest();
HttpServletResponse res = rrk.getResponse();
// Hung thread. Recover quotations from the cache
displayStockQuotations(res);
}
private Hashtable<String,Long> readStocksValue() {
Hashtable<String,Long> hash = new Hashtable<String,Long>();
hash.put("XOM", new Long(75));
hash.put("GE", new Long(1005));
hash.put("JPM", new Long(1008));
dateLastUpdate = new Date();
return hash;
}
private void displayStockQuotations(HttpServletResponse res) {
PrintWriter pw = null;
try {
pw = res.getWriter();
} catch (IOException e) {
e.printStackTrace();
}
pw.println("Quotations at :" +dateLastUpdate);
Enumeration<Long> enumStocks = hashStocks.elements();
while (enumStocks.hasMoreElements()) {
pw.println(enumStocks.nextElement());
pw.flush();
}
pw.close();
}
}
这种方法的主要好处在于,我们可以在提供应答的同时保持线程队列的执行效率。这有利于实现伸缩性较好的应用程序,并改进用户体验。
这种方法刻意保持了简单性;毕竟,本文的实际目的只是通过此示例演示一个特性。
在实际的股票系统中,我们需要添加一些复杂性,即根据用户的类型对响应消息进行自定义。比如说,与股票系统订定合约的用户或股票代理可能需要较长的超时或完全没有超时。对于所有其他用户,交付报价的合理延时需要维持在几秒钟的范围之内。
Ajax应用程序的出现极大地改变了Web应用程序的样式。使用Ajax构建的应用程序可生成内容更加丰富且更具动态性的Web站点,比如说Google Maps、MySpace、Gmail和Netflix站点。此类站点需要在底层执行大量工作,因此用户只需填写一些表单,而应用程序会自动将必要的信息提供给用户。
遗憾的是,公共Ajax最佳实践尚未开发,因此开发过程中比较容易出现各类问题。
问题在于,Web 2.0站点比Web 1.0站点更容易受到攻击:每次按键或鼠标单击操作都会转换为使用中的执行线程。因此,考虑到Ajax应用程序的这种特性,一些糟糕的机制所生产的短期HTTP请求可能会耗尽WebLogic Server的资源。并且一些恶意已经开始利用这一问题也就不足为奇了(因为只需几行代码便可实现)。在与系统管理员的对抗中,他们甚至将此类攻击称作Javascript hacking。