使用 Java 实现 Comet 风格的 Web 应用

http://www.ibm.com/developerworks/cn/web/wa-cometjava/javascript

使用 Java 实现 Comet 风格的 Web 应用html

实现 Servlet 3.0 规范
Michael Galpin, 软件架构师, eBayjava


简介: 探索 Comet 开发的不一样实现。看看 Jetty 和 Tomcat 之类的流行 Java™ Web 服务器如何支持 Comet 应用程序,并了解如何为不一样的服务器编程。最后,了解 Java 中有关 Comet 的标准化建议,这些建议是即将到来的 Servlet 3.0 和 JavaEE 6 规范的一部分。web

本文的标签:  ajax, comet, im, java, tech, web, 使用, 实现, 应用, 服务器端推技术... more tagsajax

风格的apache

标记本文!编程


发布日期: 2009 年 7 月 22 日
级别: 中级
其余语言版本: 英文
访问状况 8222 次浏览
建议: 0 (添加评论)
 平均分 (共 15 个评分 )设计模式

 

开始api

在本文中,我将展现如何使用各类不一样的 Java 技术构建一些简单的 Comet 风格的 Web 应用程序。读者对 Java servlet、Ajax 和 JavaScript 应该有必定的了解。咱们将考察 Tomcat 和 Jetty 中一些支持 Comet 的特性,所以须要使用这两个产品的最新版本。本文使用 Tomcat 6.0.14 和 Jetty 6.1.14。另外还须要一个支持 Java 5 或更高版本的 JDK。本文使用 JDK 1.5.0-16。此外还须要看看 Jetty 7 的预发布版,由于它实现了 Servlet 3.0 规范,咱们将在本文中研究该规范。请参阅 参考资料,找到下载连接。浏览器

回页首

理解 Comet

您可能已经据说过 Comet,由于它最近受到了必定的关注。Comet 有时也称反向 Ajax 或服务器端推技术(server-side push)。其思想很简单:将数据直接从服务器推到浏览器,而没必要等到浏览器请求数据。听起来简单,可是若是熟悉 Web 应用程序,尤为是 HTTP 协议,那么您就会知道,这毫不简单。实现 Comet 风格的 Web 应用程序,同时保证在浏览器和服务器上的可伸缩性,这只是在最近几年才成为可能。在本文的后面,咱们将看看一些流行的 Java Web 服务器如何支持可伸缩的 Comet 架构,但首先咱们来看看为何要建立 Comet 应用程序,以及用于实现它们的常见设计模式。

使用 Comet 的动机

HTTP 协议的成功毋庸置疑。它是 Internet 上大部分信息交换的基础。然而,它也有一些局限性。特别是,它是无状态、单向的协议。请求被发送到 Web 服务器,服务器处理请求并发回一个响应 — 仅此而已。请求必须由客户机发出,而服务器则只能在对请求的响应中发送数据。这至少会影响不少类型的 Web 应用程序的实用性。典型的例子就是聊天程序。另外还有一些例子,例如比赛的比分、股票行情或电子邮件程序。

HTTP 的这些局限性也是它取得必定成功的缘由。请求/响应周期使它成为了经典的模型,即每一个链接使用一个线程。只要可以快速为请求提供服务,这种方法就有巨大的可伸缩性。每秒钟能够处理大量的请求,只需使用少许的服务器就能够处理很大数量的用户。对于不少经典的 Web 应用程序,例如内容管理系统、搜索应用程序和电子商务站点等等而言,这很是适合。在以上任何一种 Web 应用程序中,服务器提供用户请求的数据,而后关闭链接,并释放那个线程,使之能够为其余请求服务。若是提供初始数据以后仍可能存在交互,那么将链接保持为打开状态,所以线程就不能释放出来,服务器也就不能为不少用户服务。

可是,若是想在对请求作出响应并发送初始数据以后,仍然保持与用户的交互呢?在 Web 早期,这一点常使用 meta 刷新实现。这将自动指示浏览器在指定秒数以后从新装载页面,从而支持简陋的轮询(polling)。这不只是一种糟糕的用户体验,并且一般效率很是低下。若是没有新的数据要显示在页面上呢?这时不得不从新呈现一样的页面。若是对页面的更改不多,而且页面的大部分没有变化呢?一样,无论是否有必要,都得从新请求和获取页面上的一切内容。

Ajax 的发明和流行改变了上述情况。如今,服务器能够异步通讯,所以没必要从新请求整个页面。如今能够进行增量式的更新。只需使用 XMLHttpRequest 轮询服务器。这项技术一般被称做 Comet。这项技术存在一些变体,每种变体具备不一样的性能和可伸缩性。咱们来看看这些不一样风格的 Comet。

Comet 风格

Ajax 的出现使 Comet 成为可能。HTTP 的单向性质能够有效地加以规避。实际上有一些不一样的方法能够绕过这一点。您可能已经猜到,支持 Comet 的最容易的方式是轮询(poll)。使用 XMLHttpRequest 向服务器发出调用,返回后,等待一段固定的时间(一般使用 JavaScript 的 setTimeout 函数),而后再次调用。这是一项很是常见的技术。例如,大多数 webmail 应用程序就是经过这种技术在电子邮件到达时显示电子邮件的。

这项技术有优势也有缺点。在这种状况下,您指望快速返回响应,就像任何其余 Ajax 请求同样。在请求之间必须有一段暂停。不然,接二连三的请求会冲垮服务器,而且这种状况下显然不具备可伸缩性。这段暂停使应用程序产生一个延时。暂停的时间越长,服务器上的新数据就须要越多的时间才能到达客户机。若是缩短暂停时间,又将从新面临冲垮服务器的风险。可是另外一方面,这显然是最简单的实现 Comet 的方式。

如今应该指出,不少人认为轮询并不属于 Comet。相反,他们认为 Comet 是对轮询的局限性的一个解决方案。最多见的 “真正的” Comet 技术是轮询的一种变体,即长轮询(long polling)。轮询与长轮询之间的主要区别在于服务器花多长的时间做出响应。长轮询一般将链接保持一段较长的时间 — 一般是数秒钟,可是也多是一分钟甚至更长。当服务器上发生某个事件时,响应被发送并随即关闭,轮询当即从新开始。

长轮询相对于通常轮询的优势在于,数据一旦可用,便当即从服务器发送到客户机。请求可能等待较长的时间,期间没有任何数据返回,可是一旦有了新的数据,它将当即被发送到客户机。所以没有延时。若是您使用过基于 Web 的聊天程序,或者声称 “实时” 的任何程序,那么它极可能就是使用了这种技术。

长轮询有一种变体,这是第三种风格的 Comet。这一般被称为流(streaming)。按照这种风格,服务器将数据推回客户机,可是不关闭链接。链接将一直保持开启,直到过时,并致使从新发出请求。XMLHttpRequest 规范代表,能够检查 readyState 的值是否为 3 或 Receiving(而不是 4 或 Loaded),并获取正从服务器 “流出” 的数据。和长轮询同样,这种方式也没有延时。当服务器上的数据就绪时,该数据被发送到客户机。这种方式的另外一个优势是能够大大减小发送到服务器的请求,从而避免了与设置服务器链接相关的开销和延时。不幸的是,XMLHttpRequest 在不一样的浏览器中有不少不一样的实现。这项技术只能在较新版本的 Mozilla Firefox 中可靠地使用。对于 Internet Explorer 或 Safari,仍需使用长轮询。

至此,您可能会想,长轮询和流都有一个很大的问题。请求须要在服务器上存在一段较长的时间。这打破了每一个请求使用一个线程的模型,由于用于一个请求的线程一直没有被释放。更糟糕的是,除非要发回数据,不然该线程一直处于空闲状态。这显然不具备可伸缩性。幸运的是,现代 Java 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
<!-- This is the usual Connector, comment it out and add the NIO one -->
   <!-- Connector URIEncoding="utf-8" connectionTimeout="20000" port="8084"
protocol="HTTP/1.1" redirectPort="8443"/ -->
<Connector connectionTimeout="20000" port="8080" protocol="org.apache.
coyote.http11.Http11NioProtocol" redirectPort="8443"/>

 

这使 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<String> messages = new ArrayList<String>();
    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] + "<br>";
                    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<URL> zipCodes;
    private final String YAHOO_WEATHER = "http://weather.yahooapis.com/forecastrss?p=";

    public Weatherman(Integer... zips) {
        zipCodes = new ArrayList<URL>(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("<h2>");
        html.append(entry.getTitle());
        html.append("</h2>");
        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" 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 标准化的升级路线。


回页首

下载描述 名字 大小 下载方法
Weather Server 源代码 WeatherServer.zip 347KB HTTP


关于下载方法的信息


参考资料

学习
 “Comet 的诱惑”:发现更多关于 Comet 背后的历史和动机的信息。

 “经过 Tomcat Advanced I/O 得到高性能的 Ajax”:了解更多关于 Tomcat 上的 Comet 的信息。

 “经过 Tomcat Advanced I/O 得到高性能的 Ajax”:Direct Web Remoting(DWR)可让 Ajax 对于 Java 开发人员来讲容易得多,而且能够为 Comet 作一样的事情。可经过这篇 developerWorks 文章详细了解。

 “使用 Jetty 和 Direct Web Remoting 编写可扩展的 Comet 应用程序”:深刻研究 Comet 的性能影响。

 “为 WebSphere Application Server Community Edition 开发富 Internet 应用程序 ”:看 Comet 如何在完整的 Java EE 栈中一样运行良好。

Asynchronous HTTP Comet architectures:查看 Asynchronous HTTP Comet 架构,了解更多关于 Java 中的 Comet 的信息。

An Introduction to Servlet 3.0:了解 Servlet 3.0 的全部新特性。

 查阅关于 Tomcat 的 Advanced IO 和 Jetty 中的 Continuations 的参考信息。

“使用 Eclipse 在 Google App Engine 上建立 mashup,第 2 部分: 构建 Ajax mashup”:看如何使用 Python 和 Prototype JavaScript 库建立可部署到 Google App Engine 的 Comet 应用程序。

Web development 专区:访问 developerWorks Web development 专区,这里有不少技术文章和技巧、教程以及标准。

developerWorks podcasts:收听有关软件开发人员的有趣访谈和讨论。

developerWorks 技术活动和网络广播:随时关注 developerWorks 技术活动和网络广播。


得到产品和技术
 本文使用了 JDK 1.5.0-16。

Jetty:本文使用的是 Jetty 6.1.14。

Tomcat:本文使用的是 Tomcat 6.0.14。

 下载 IBM 产品评估版,得到来自 DB2®、 Lotus®、Rational®、Tivoli® 和 WebSphere® 的应用程序开发工具和中间件产品。


讨论
 加入 developerWorks 社区。


关于做者

Michael Galpin 从 1998 年开始从事 Java 软件开发。他目前在 Ludi Labs 工做,这是在加利福尼亚州芒廷维尤市新成立的公司。他从 California Institute of Technology 得到

相关文章
相关标签/搜索