`

Java中如何实现Comet风格的Web应用(二)

    博客分类:
  • Web
阅读更多

 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);
  log("Message added #messages=" + messages.size());
  messages.notify();
  }
  }
  public void run() {
  while (running) {
  if (messages.size() == 0) {
  try {
  synchronized (messages) {
  messages.wait();
  }
  } catch (InterruptedException e) {
  // Ignore
  }
  }
  String[] pendingMessages = null;
  synchronized (messages) {
  pendingMessages = messages.toArray(new String[0]);
  messages.clear();
  }
  try {
  if (connection == null){
  try{
  synchronized(this){
  wait();
  }
  } catch (InterruptedException e){
  // Ignore
  }
  }
  PrintWriter writer = connection.getWriter();
  for (int j = 0; j < pendingMessages.length; j++) {
  final String forecast = pendingMessages[j] + "
";
  writer.println(forecast);
  log("Writing:" + forecast);
  }
  writer.flush();
  writer.close();
  connection = null;
  log("Closing connection");
  } catch (IOException e) {
  log("IOExeption sending message", e);
  }
  }
  }
  }

  这个类基本上是样板代码,与 Comet 没有直接的关系。但是,有两点要注意。这个类含有一个 ServletResponse 对象。回头看看清单 2 中的 event 方法,当事件为 BEGIN 时,response 对象被传入到 MessageSender 中。在 MessageSender 的 run 方法中,它使用 ServletResponse 将数据发送回客户机。注意,一旦发送完所有排队等待的消息后,它将关闭连接。这样就实现了长轮询。如果要实现流风格的 Comet,那么需要使连接保持开启,但是仍然刷新数据。

  回头看清单 2 可以发现,其中创建了一个 Weatherman 类。正是这个类使用 MessageSender 将数据发送回客户机。这个类使用 Yahoo RSS feed 获得不同地区的天气信息,并将该信息发送到客户机。这是一个特别设计的例子,用于模拟以异步方式发送数据的数据源。清单 4 显示了它的代码。

  清单 4. Weatherman

 private class Weatherman implements Runnable{
  private final List zipCodes;
  private final String YAHOO_WEATHER =  "http://weather.yahooapis.com/forecastrss?p=";
  public Weatherman(Integer... zips) {
  zipCodes = new ArrayList(zips.length);
  for (Integer zip : zips) {
  try {
  zipCodes.add(new URL(YAHOO_WEATHER + zip));
  } catch (Exception e) {
  // dont add it if it sucks
  }
  }
  }
  public void run() {
  int i = 0;
  while (i >= 0) {
  int j = i % zipCodes.size();
  SyndFeedInput input = new SyndFeedInput();
  try {
  SyndFeed feed = input.build(new InputStreamReader(zipCodes.get(j)
  .openStream()));
  SyndEntry entry = (SyndEntry) feed.getEntries().get(0);
  messageSender.send(entryToHtml(entry));
  Thread.sleep(30000L);
  } catch (Exception e) {
  // just eat it, eat it
  }
  i++;
  }
  }
  private String entryToHtml(SyndEntry entry){
  StringBuilder html = new StringBuilder("
");
  html.append(entry.getTitle());
  html.append("
");
  html.append(entry.getDescription().getValue());
  return html.toString();
  }
  }

  这个类使用 Project Rome 库解析来自 Yahoo Weather 的 RSS feed。如果需要生成或使用 RSS 或 Atom feed,这是一个非常有用的库。此外,这个代码中只有一个地方值得注意,那就是它产生另一个线程,用于每过 30 秒钟发送一次天气数据。最后,我们再看一个地方:使用该 Servlet 的客户机代码。在这种情况下,一个简单的 JSP 加上少量的 JavaScript 就足够了。清单 5 显示了该代码。

  清单 5. 客户机 Comet 代码

   <%@page contentType="text/html" pageEncoding="UTF-8"%> 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
  "http://www.w3.org/TR/html4/loose.dtd"> 
 
<html> 
  <head> 
    <meta http-equiv="Content-Type" content="text/html;  charset=UTF-8"> 
    <title>Comet Weather</title> 
    <SCRIPT TYPE="text/Javascript"> 
      function go(){ 
        var url = "http://localhost:8484/WeatherServer/Weather" 
        var request = new XMLHttpRequest(); 
        request.open("GET", url, true); 
         request.setRequestHeader("Content-Type","application/x-javascript;"); 
        request.onreadystatechange = function() { 
          if (request.readyState == 4) { 
            if (request.status == 200){ 
              if (request.responseText) { 
                document.getElementById("forecasts").innerHTML = 
request.responseText; 
              } 
            } 
            go(); 
          } 
        }; 
        request.send(null); 
      } 
    </SCRIPT> 
  </head> 
  <body> 
    <h1>Rapid Fire Weather</h1> 
    <input type="button" onclick="go()" value="Go!"></input>  
    <div id="forecasts"></div> 
  </body> 
</html> 

   该代码只是在用户单击 Go 按钮时开始长轮询。注意,它直接使用 XMLHttpRequest 对象,所以这在 Internet Explorer 6 中将不能工作。您可能需要使用一个 Ajax 库解决浏览器差异问题。除此之外,惟一需要注意的是回调函数,或者为请求的 onreadystatechange 函数创建的闭包。该函数粘贴来自服务器的新的数据,然后重新调用 go 函数。

  现在,我们看过了一个简单的 Comet 应用程序在 Tomcat 上是什么样的。有两件与 Tomcat 密切相关的事情要做:一是配置它的连接器,二是在 Servlet 中实现一个特定于 Tomcat 的接口。您可能想知道,将该代码 “移植” 到 Jetty 有多大难度。接下来我们就来看看这个问题。

  Jetty 和 Comet

  Jetty 服务器使用稍微不同的技术来支持 Comet 的可伸缩的实现。Jetty 支持被称作 continuations 的编程结构。其思想很简单。请求先被暂停,然后在将来的某个时间点再继续。规定时间到期,或者某种有意义的事件发生,都可能导致请求继续。当请求被暂停 时,它的线程被释放。

  可以使用 Jetty 的 org.mortbay.util.ajax.ContinuationSupport 类为任何 HttpServletRequest 创建 org.mortbay.util.ajax.Continuation 的一个实例。这种方法与 Comet 有很大的不同。但是,continuations 可用于实现逻辑上等效的 Comet。清单 6 显示清单 2 中的 weather servlet “移植” 到 Jetty 后的代码。

  清单 6. Jetty Comet servlet

  public class JettyWeatherServlet extends HttpServlet {
  private MessageSender messageSender = null;
  private static final Integer TIMEOUT = 5 * 1000;
  public void begin(HttpServletRequest request, HttpServletResponse  response)
  throws IOException, ServletException {
  request.setAttribute("org.apache.tomcat.comet", Boolean.TRUE);
  request.setAttribute("org.apache.tomcat.comet.timeout", TIMEOUT);
  messageSender.setConnection(response);
  Weatherman weatherman = new Weatherman(95118, 32408);
  new Thread(weatherman).start();
  }
  public void end(HttpServletRequest request, HttpServletResponse  response)
  throws IOException, ServletException {
  synchronized (request) {
  request.removeAttribute("org.apache.tomcat.comet");
  Continuation continuation = ContinuationSupport.getContinuation
  (request, request);
  if (continuation.isPending()) {
  continuation.resume();
  }
  }
  }
  public void error(HttpServletRequest request, HttpServletResponse  response)
  throws IOException, ServletException {
  end(request, response);
  }
  public boolean read(HttpServletRequest request, HttpServletResponse  response)
  throws IOException, ServletException {
  throw new UnsupportedOperationException();
  }
  @Override
  protected void service(HttpServletRequest request, HttpServletResponse  response)
  throws IOException, ServletException {
  synchronized (request) {
  Continuation continuation = ContinuationSupport.getContinuation
  (request, request);
  if (!continuation.isPending()) {
  begin(request, response);
  }
  Integer timeout = (Integer) request.getAttribute
  ("org.apache.tomcat.comet.timeout");
  boolean resumed = continuation.suspend(timeout == null ? 10000 :
  timeout.intValue());
  if (!resumed) {
  error(request, response);
  }
  }
  }
  public void setTimeout(HttpServletRequest request, HttpServletResponse  response,
  int timeout) throws IOException, ServletException,
  UnsupportedOperationException {
  request.setAttribute("org.apache.tomcat.comet.timeout", new  Integer(timeout));
  }
  }

  这里最需要注意的是,该结构与 Tomcat 版本的代码非常类似。begin、read、end 和 error 方法都与 Tomcat 中相同的事件匹配。该 Servlet 的 service 方法被覆盖为在请求第一次进入时创建一个 continuation 并暂停该请求,直到超时时间已到,或者发生导致它重新开始的事件。上面没有显示 init 和 destroy 方法,因为它们与 Tomcat 版本是一样的。该 servlet 使用与 Tomcat 相同的 MessageSender。因此不需要修改。注意 begin 方法如何创建 Weatherman 实例。对这个类的使用与 Tomcat 版本中也是完全相同的。甚至客户机代码也是一样的。只有 servlet 有更改。虽然 servlet 的变化比较大,但是与 Tomcat 中的事件模型仍是一一对应的。

  希望这足以鼓舞人心。虽然完全相同的代码不能同时在 Tomcat 和 Jetty 中运行,但是它是非常相似的。当然,JavaEE 吸引人的一点是可移植性。大多数在 Tomcat 中运行的代码,无需修改就可以在 Jetty 中运行,反之亦然。因此,毫不奇怪,下一个版本的 Java Servlet 规范包括异步请求处理(即 Comet 背后的底层技术)的标准化。 我们来看看这个规范:Servlet 3.0 规范。

  Servlet 3.0 规范

  在此,我们不深究 Servlet 3.0 规范的全部细节,只看看 Comet servlet 如果在 Servlet 3.0 容器中运行,可能会是什么样子。注意 “可能” 二字。该规范已经发布公共预览版,但在撰写本文之际,还没有最终版。因此,清单 7 显示的是遵从公共预览规范的一个实现。

  清单 7. Servlet 3.0 Comet

  @WebServlet(asyncSupported=true, asyncTimeout=5000)
  public class WeatherServlet extends HttpServlet {
  private MessageSender messageSender;
  // init and destroy are the same as other
  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse  response)
  throws ServletException, IOException {
  AsyncContext async = request.startAsync(request, response);
  messageSender.setConnection(async);
  Weatherman weatherman = new Weatherman(95118, 32444);
  async.start(weatherman);;
  }
  }

  值得高兴的是,这个版本要简单得多。平心而论,如果不遵从 Tomcat 的事件模型,在 Jetty 中可以有类似的实现。这种事件模型似乎比较合理,很容易在 Tomcat 以外的容器(例如 Jetty)中实现,只是没有相关的标准。

  回头看看清单 7,注意它的标注声明它支持异步处理,并设置了超时时间。startAsync 方法是 HttpServletRequest 上的一个新方法,它返回新的 javax.servlet.AsyncContext 类的一个实例。注意,MessageSender 现在传递 AsynContext 的引用,而不是 ServletResponse 的引用。在这里,不应该关闭响应,而是调用 AsyncContext 实例上的 complete 方法。还应注意,Weatherman 被直接传递到 AsyncContext 实例的 start 方法。这样将在当前 ServletContext 中开始一个新线程。

  而且,尽管与 Tomcat 或 Jetty 相比都有较大的不同,但是修改相同风格的编程来处理 Servlet 3.0 规范提议的 API 并不是太难。还应注意,Jetty 7 是为实现 Servlet 3.0 而设计的,目前处于 beta 状态。但是,在撰写本文之际,它还没有实现该规范的最新版本。

  结束语

  Comet 风格的 Web 应用程序可以为 Web 带来全新的交互性。它为大规模地实现这些特性带来一些复杂的挑战。但是,领先的 Java Web 服务器正在为实现 Comet 提供成熟、稳定的技术。在本文中,您看到了 Tomcat 和 Jetty 上当前风格的 Comet 的不同点和相似点,以及正在进行的 Servlet 3.0 规范的标准化。Tomcat 和 Jetty 使如今构建可伸缩的 Comet 应用程序成为可能,并且明确了未来面向 Servlet 3.0 标准化的升级路线。

分享到:
评论

相关推荐

    java-comet

    Java-Comet是一种在Java...总结,Java-Comet技术是Java环境中实现服务器向客户端实时推送数据的一种方法,尤其适用于需要实时交互的Web应用。通过理解和掌握Java-Comet,开发者可以创建出更高效、响应更快的Web服务。

    Java 实现 Comet 长连接,服务器主动发送消息给客户端

    通过理解和实践 Java 实现的 Comet 长连接,开发者可以构建出高效、实时的 Web 应用,提升用户体验。同时,随着 WebSocket 的普及,它逐渐成为现代实时通信的首选技术,但 Comet 仍然是在某些场景下不可或缺的解决...

    使用Java实现类似Comet风格的web app

    要使用 Java 实现类似 Comet 风格的 Web 应用,你需要对以下几个关键知识点有所了解: 1. **HTTP 协议的限制**: - HTTP 协议是基于请求-响应模型的,意味着客户端(浏览器)需要先发送请求,服务器才能响应。 - ...

    web推送 comet技术

    Comet4J是一个实现了Comet技术的Java框架,它简化了在Java应用中实现服务器推送的复杂性。使用Comet4J,开发者可以创建服务端的推送通道,通过这些通道,服务器能够将数据高效地推送到多个客户端。Comet4J提供了多种...

    浅析Comet技术在Java Web实时系统开发中的应用.pdf

    浅析Comet技术在Java Web实时系统开发中的应用 Comet技术是Java Web实时系统开发中的关键技术之一,它通过实现服务器推送(Server Push)来解决AJAX需要定时频繁发送请求的问题。Comet技术有两种实现方式:长轮询...

    ssm.rar_comet_java comet_java comet推送_聊天 JAVA SSM

    在这个"ssm.rar_comet_java"压缩包中,我们聚焦于Comet技术在Java环境下的应用,特别是用于实现服务器推送功能,如聊天应用。 Comet是一种Web实时通信技术,它通过持久化HTTP连接使得服务器可以主动向客户端推送...

    Servlet3.0 异步处理 页面推送 Comet 实例

    在传统的HTTP协议中,服务器只能在客户端发起请求时发送数据,而Comet通过长时间保持一个HTTP连接开放,使得服务器可以在需要时主动推送数据,极大地增强了Web应用的实时性。 在Servlet3.0之前,实现Comet技术通常...

    Comet Web 应用

    Comet技术,全称为“Comet Web应用”,是一种在Web开发中实现服务器向客户端推送数据的技术,也被称为反向Ajax或服务器端推送。这种技术打破了传统的HTTP请求/响应模式,使得服务器可以在客户端未发起新请求时主动...

    面向 Java 开发人员的 Ajax: 使用 Jetty 和 Direct Web Remoting 编写可扩展的 Comet 应用程序

    面向Java开发人员的Ajax技术...总结,使用Jetty和DWR来构建Comet应用,Java开发者可以实现双向通信,提供更具互动性的Web体验。通过学习和实践,你可以创建出高效、可扩展的实时应用程序,满足各种现代Web场景的需求。

    Asp.net Comet(Web)技术

    Comet技术的核心目标是解决Web应用中的实时性问题,如股票报价、聊天室、在线游戏等场景,需要即时更新信息。由于HTTP协议本身的限制,Comet采用了两种主要的实现策略:长轮询(Long-Polling)和流(Streaming)。 ...

    javaweb实现后台向前台的消息推送 comet4j

    Comet4j是一个专门用于JavaWeb应用的长连接通讯框架,它简化了基于Comet技术的实时通信实现。 首先,我们要理解Comet技术。Comet是一种让服务器端能够长时间保持HTTP连接打开的技术,使得服务器可以在适当的时候向...

    java comet服务器推送使用步骤

    Comet作为一种实现服务器向客户端推送数据的技术方案,在早期的Web应用中扮演了重要的角色。Java Comet框架为开发者提供了一种简单而有效的方式来实现这一功能。本文将详细介绍如何使用Comet4J框架来实现Java Comet...

    DWR+JAVA进行web消息推送dwr-comet.zip

    在本文中,我们将深入探讨如何使用DWR(Direct Web Remoting)与Java技术结合实现Web消息推送功能。DWR是一种JavaScript库,它允许在浏览器和服务器之间进行实时、双向通信,为Web应用提供了类似AJAX的功能,但更加...

    dwr_comet_im.rar_DEMO_comet.tld_java comet

    综上所述,这个压缩包提供了DWR实现Comet技术的一个实践示例,可以帮助开发者理解如何在Java Web应用中构建即时通讯功能。通过学习和分析这个DEMO,我们可以掌握如何配置DWR,使用comet.tld标签库,以及编写客户端和...

    浅析Comet技术在Java Web实时系统开发中的应用.docx

    Comet技术是为了解决Java Web实时系统中服务器推送数据到客户端的问题,它克服了HTTP协议的请求-响应模式的局限性。在传统的HTTP模式下,客户端必须发起请求才能获取服务器的数据,而在实时系统中,如新闻更新、股票...

    C#Web即时通讯Comet框架

    "我的示例"(MyDemo)可能是包含在压缩包中的一个实际应用案例,展示了如何使用这个Comet框架来实现Web即时通讯。这个例子可能包括服务器端代码和客户端JavaScript代码,通过创建连接、发送和接收消息,以及处理推送...

    浅析comet技术在java web实时系统开发中的应用.doc

    在Java Web环境中,使用Comet技术通常需要结合Servlet或WebSocket等技术。长轮询方式利用Servlet的生命周期,服务器接收到请求后保持连接,直到有新数据或超时才返回响应。流方式则可能需要更底层的控制,如使用...

    comet demo 向客户端推送例子

    总的来说,这个"comet demo"是一个学习和理解Comet技术的绝佳实践,可以帮助开发者掌握如何在Java Web环境中实现服务器向客户端的实时数据推送。通过深入研究和运行这个示例,你可以更好地了解HTTP长连接的工作原理...

    基于Comet属性同步的Java Web实时进度条研究.pdf

    本文研究了基于Comet属性同步的Java Web实时进度条的实现方案。该方案通过结合Comet技术和Java的反射技术,实现了Java Web实时进度条的处理方式。该方案的优势在于纯Java实现、代码简单、无需安装浏览器插件,并具有...

Global site tag (gtag.js) - Google Analytics