Servlet3规范支持异步请求(或者称为长连接,或者反向AJAX,或者COMET,或者服务器推送技术):无阻塞的输入与输出模型,可以延时的请求和响应功能,还有超时事件通知,看上去一切都是那么完美。
但终端浏览器支持长连接情况差强人意,对Comet的支持大致汇总如下:
- IE浏览器最佳实践是使用htmlfile ActiveXObject,以及创建隐藏IFrame组件,可以跨越IE6-IE8;虽IE 8支持XDomainRequest支持HTTP Streaming,但仅仅是IE 8。
- Firefox 浏览器相当棒,支持XMLHttpRequest Streaming 和隐藏的IFrame组件。
- Safari 浏览器支持XMLHttpRequest Streaming。
- Chrome有些无奈,算不上支持XMLHttpRequest Streaming,使用IFrame的话会一直出现正在加载中的标志。
- Opera也不支持XMLHttpRequest Streaming,使用IFrame的话会一直出现正在加载中的标志。
总之,使用IFrame是一个不错的方案,在IE、Firefox下表现的很完美,在其它浏览器下只能忍受讨厌的正在加载中。数据交换格式可以采用JS脚本调用。
但无论哪一种方案,都必须认识到,一个持久的连接,当页面内容一直在递增时,会越来越膨胀,会占用用户机器的CPU,尽量隔一段时间断开连接,重新请求。
HTTP 1.1规范中声明客户端不应该与服务器端建立超过两个 HTTP 连接,因此浏览器内需要借助脚本避免客户重开两个脚本。
按照目前情形下,需要借助AJAX PULL  + COMET PUSH 相结合来打造相当好的用户体验。
Servlet本身,无论2.4或者2.5的版本,可以使用一个循环达到长连接的目标:
/**
 * 一个典型的长连接实现
 * 
 * @author yongboy
 * @date 2011-1-14
 * @version 1.0
 */
@WebServlet("/demoLongLink")
public class DemoLongLinkServlet extends HttpServlet {
 private static final long serialVersionUID = 4617227991063927036L;
 protected void doGet(HttpServletRequest request,
   HttpServletResponse response) throws ServletException, IOException {
  response.setHeader("Cache-Control", "private");
  response.setHeader("Pragma", "no-cache");
  response.setHeader("Connection", "Keep-Alive");
  response.setHeader("Proxy-Connection", "Keep-Alive");
  response.setContentType("text/html;charset=UTF-8");
  PrintWriter out = response.getWriter();
  out.println("<div>Start ...</div>");
  out.flush();
  int num = 0;
  int max = 100;
  while (true) {
   out.println("<div>" + (num++) + "</div>");
   out.flush();
   if (num >= max) {
    break;
   }
   try {
    Thread.sleep(1000);
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
  }
  out.println("<div>Done !</div>");
  out.flush();
  out.close();
 }
}
每一个连接线程都处于一个不断循环之中,不能够有效释放,相当的浪费服务器资源,有可能导致容器内线程池耗尽,将无法应对后续请求。同时少了异步连接的特性,无法直接定义超时时间,更不要说超时事件,超时监听器等企业特性了。
当然也可以实现异步请求,但可能没有规范那般严格。
同步请求的模型:
对比异步请求模型:
在前两篇文章中,使用一个单独线程处理资源,分发到大部分的异步请求中。