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 得到