使用Java实现Comet风格的Web应用(二)

2014-11-24 01:40:16 · 作者: · 浏览: 6
va Web 服务器有很多方式可以解决这个问题。

回页首

Java 中的Comet

现在有很多Web 服务器是用Java 构建的。一个原因是Java 有一个丰富的本地线程模型。因此实现典型的每个连接一个线程的模型便非常简单。该模型对于Comet 不大适用,但是,Java 对此同样有解决的办法。为了有效地处理Comet,需要非阻塞IO,Java 通过它的NIO 库提供非阻塞IO。两种最流行的开源服务器Apache Tomcat 和Jetty 都利用NIO 增加非阻塞IO,从而支持Comet。然而,这两种服务器中的实现却各不相同。我们来看看Tomcat 和Jetty 对Comet 的支持。

Tomcat 和Comet

对于Apache Tomcat,要使用Comet,主要需要做两件事。首先,需要对Tomcat 的配置文件server.xml 稍作修改。默认情况下启用的是更典型的同步IO 连接器。现在只需将它切换成异步版本,如清单1 所示。

清单1. 修改Tomcat 的server.xml

这使Tomcat 可以处理更多的并发连接,但需要说明的是,其中大多数连接有很多时间都处于空闲状态。利用这一点的最容易的方式是创建一个实现org.apache.catalina.CometProcessor 接口的servlet。这显然是Tomcat 特有的一个接口。清单2 显示了一个这样的例子。

清单2. Tomcat Comet servlet

public class TomcatWeatherServlet extends HttpServlet implements CometProcessor {

private MessageSender messageSender = null;

private static final Integer TIMEOUT = 60 * 1000;

@Override

public void destroy() {

messageSender.stop();

messageSender = null;

}

@Override

public void init() throws ServletException {

messageSender = new MessageSender();

Thread messageSenderThread =

new Thread(messageSender, "MessageSender[" + getServletContext()

.getContextPath() + "]");

messageSenderThread.setDaemon(true);

messageSenderThread.start();

}

public void event(final CometEvent event) throws IOException, ServletException {

HttpServletRequest request = event.getHttpServletRequest();

HttpServletResponse response = event.getHttpServletResponse();

if (event.getEventType() == CometEvent.EventType.BEGIN) {

request.setAttribute("org.apache.tomcat.comet.timeout", TIMEOUT);

log("Begin for session: " + request.getSession(true).getId());

messageSender.setConnection(response);

Weatherman weatherman = new Weatherman(95118, 32408);

new Thread(weatherman).start();

} else if (event.getEventType() == CometEvent.EventType.ERROR) {

log("Error for session: " + request.getSession(true).getId());

event.close();

} else if (event.getEventType() == CometEvent.EventType.END) {

log("End for session: " + request.getSession(true).getId());

event.close();

} else if (event.getEventType() == CometEvent.EventType.READ) {

throw new UnsupportedOperationException("This servlet does not accept

data");

}

}

}

CometProcessor 接口要求实现event 方法。这是用于Comet 交互的一个生命周期方法。Tomcat 将使用不同的CometEvent 实例调用。通过检查CometEvent 的eventType,可以判断正处在生命周期的哪个阶段。当请求第一次传入时,即发生BEGIN 事件。READ 事件表明数据正在被发送,只有当请求为POST 时才需要该事件。遇到END 或ERROR 事件时,请求终止。

在清单2 的例子中,servlet 使用一个MessageSender 类发送数据。这个类的实例是在servlet 的init 方法中在其自身的线程中创建,并在servlet 的destroy 方法中销毁的。清单3 显示了MessageSender。

清单3. MessageSender

private class MessageSender implements Runnable {

protected boolean running = true;

protected final ArrayList messages = new ArrayList();

private ServletResponse connection;

private synchronized void setConnection(ServletResponse connection){

this.connection = connection;

notify();

}

public void send(String message) {

synchronized (messages) {

messages.add(message);