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();
}
}
每一个连接线程都处于一个不断循环之中,不能够有效释放,相当的浪费服务器资源,有可能导致容器内线程池耗尽,将无法应对后续请求。同时少了异步连接的特性,无法直接定义超时时间,更不要说超时事件,超时监听器等企业特性了。
当然也可以实现异步请求,但可能没有规范那般严格。
同步请求的模型:
对比异步请求模型:
在前两篇文章中,使用一个单独线程处理资源,分发到大部分的异步请求中。