前不久
自己用C实现的comet http server,基于libevent,用c实现了一个comet server原型,由于libevent封装了一个http协议实
现,代码比较简洁,但不简单。最近又了解了一下Java的实现方案。
- Java中目前有2种选择,一种是基于Apache MINA框架的,稍复杂但适合用在大场合;一种是基于Java Web容器自己提供的支持,如Jetty和Tomcat,适合comet只是应用中一小部分场合。
- Mina只是个网络层(相当socket层)的框架,如果用mina需要自己实现HTTP协议。好消息是这个工作已经有开源做了,一个叫Asyncweb项目在mina的基础上实现了HTTP协议层的封装。而且asyncweb正在被mina合并,将加入到mina的发行版中。
用asyncweb编程很简单,只需实现一个接口,实际上只需要实现 handleRequest, 在 request 返回结果。
public interface HttpService
{
void handleRequest( HttpServiceContext context ) throws Exception;
void start();
void stop();
}
- 经过测试,asyncweb适合做异步long pooling,但不适合做Streaming XHR, 参看一下例子
handleRequest 例子,不管是同步还是异步,都必须一步输出,不支持一次写一部分
/** 同步处理的例子 */
public void handleRequest( HttpServiceContext context ) throws Exception {
MutableHttpResponse response = new DefaultHttpResponse();
StringWriter buf = new StringWriter();
PrintWriter writer = new PrintWriter(buf);
writer.println("test");
writer.flush();
IoBuffer bb = IoBuffer.allocate(1024);
bb.setAutoExpand(true);
bb.putString(buf.toString(), Charset.forName("UTF-8").newEncoder());
bb.flip();
response.setContent(bb);
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
response.setStatus(HttpResponseStatus.OK);
context.commitResponse(response);
}
/** 异步处理的例子,可以不马上返回,但是必须一次返回 */
public void handleRequest( HttpServiceContext context ) throws Exception {
context.addClientListener( new HttpClientListener() {
public void clientDisconnected( HttpServiceContext ctx ) {
}
public void clientIdle( HttpServiceContext ctx, long idleTime, int idleCount ) {
// do something...
// context.commitResponse(...)
}
});
}
所以Asyncweb如果需要Streaming XHR功能还需要修改source code,所以目前成熟的做法还是用Jetty or Tomcat,另外在网上见到说GlassFish中的
Grizzly使用修改了asyncweb实现了comet功能,或许那个1年多前的修改版本更值得借鉴。
Jetty 的Comet实现从编程的角度是最简单的,suspend的时候本次执行就结束了。
public class ForeverFrameJettyServlet extends HttpServlet {
public void service(HttpServletRequest req, HttpServletResponse resp) throws IOException {
// handle the time request
if (req.getRequestURI().endsWith("/time")) {
// get the jetty continuation
Continuation cc = ContinuationSupport.getContinuation(req, null);
// set the header
resp.setContentType("text/html");
// write time periodically
while (true) {
cc.suspend(1000); // suspend the response
resp.getWriter().println("test");
resp.getWriter().flush();
}
}
// ...
}
}
Tomcat6 也支持Comet,需要实现一个
CometProcessor接口,稍繁琐。例子可参考下面Resource1
Resource:
1. Asynchronous HTTP and Comet architectures
An introduction to asynchronous, non-blocking HTTP programming
http://www.javaworld.com/javaworld/jw-03-2008/jw-03-asynchhttp.html
2.
AsyncWeb Performance Test 与Apache2性能比较