欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

使用Java實(shí)現(xiàn)類似Comet風(fēng)格的web app

 更新時(shí)間:2015年11月23日 15:44:54   作者:turkeyzhou  
這篇文章主要介紹了使用Java實(shí)現(xiàn)類似Comet風(fēng)格的web app的方法,包括客戶端的響應(yīng)和XML解析等功能,需要的朋友可以參考下

開始
    在本文中,我將展示如何使用各種不同的 Java 技術(shù)構(gòu)建一些簡(jiǎn)單的 Comet 風(fēng)格的 Web 應(yīng)用程序。讀者對(duì) Java Servlet、Ajax 和 JavaScript 應(yīng)該有一定的了解。我們將考察 Tomcat 和 Jetty 中一些支持 Comet 的特性,因此需要使用這兩個(gè)產(chǎn)品的最新版本。本文使用 Tomcat 6.0.14 和 Jetty 6.1.14.另外還需要一個(gè)支持 Java 5 或更高版本的 JDK.本文使用 JDK 1.5.0-16.此外還需要看看 Jetty 7 的預(yù)發(fā)布版,因?yàn)樗鼘?shí)現(xiàn)了 Servlet 3.0 規(guī)范,我們將在本文中研究該規(guī)范。
    理解 Comet
    您可能已經(jīng)聽說過 Comet,因?yàn)樗罱艿搅艘欢ǖ年P(guān)注。Comet 有時(shí)也稱反向 Ajax 或服務(wù)器端推技術(shù)(server-side push)。其思想很簡(jiǎn)單:將數(shù)據(jù)直接從服務(wù)器推到瀏覽器,而不必等到瀏覽器請(qǐng)求數(shù)據(jù)。聽起來簡(jiǎn)單,但是如果熟悉 Web 應(yīng)用程序,尤其是 HTTP 協(xié)議,那么您就會(huì)知道,這絕不簡(jiǎn)單。實(shí)現(xiàn) Comet 風(fēng)格的 Web 應(yīng)用程序,同時(shí)保證在瀏覽器和服務(wù)器上的可伸縮性,這只是在最近幾年才成為可能。在本文的后面,我們將看看一些流行的 Java Web 服務(wù)器如何支持可伸縮的 Comet 架構(gòu),但首先我們來看看為什么要?jiǎng)?chuàng)建 Comet 應(yīng)用程序,以及用于實(shí)現(xiàn)它們的常見設(shè)計(jì)模式。
    使用 Comet 的動(dòng)機(jī)
    HTTP 協(xié)議的成功毋庸置疑。它是 Internet 上大部分信息交換的基礎(chǔ)。然而,它也有一些局限性。特別是,它是無狀態(tài)、單向的協(xié)議。請(qǐng)求被發(fā)送到 Web 服務(wù)器,服務(wù)器處理請(qǐng)求并發(fā)回一個(gè)響應(yīng) — 僅此而已。請(qǐng)求必須由客戶機(jī)發(fā)出,而服務(wù)器則只能在對(duì)請(qǐng)求的響應(yīng)中發(fā)送數(shù)據(jù)。這至少會(huì)影響很多類型的 Web 應(yīng)用程序的實(shí)用性。典型的例子就是聊天程序。另外還有一些例子,例如比賽的比分、股票行情或電子郵件程序。
    HTTP 的這些局限性也是它取得一定成功的原因。請(qǐng)求/響應(yīng)周期使它成為了經(jīng)典的模型,即每個(gè)連接使用一個(gè)線程。只要能夠快速為請(qǐng)求提供服務(wù),這種方法就有巨大的可伸縮性。每秒鐘可以處理大量的請(qǐng)求,只需使用少量的服務(wù)器就可以處理很大數(shù)量的用戶。對(duì)于很多經(jīng)典的 Web 應(yīng)用程序,例如內(nèi)容管理系統(tǒng)、搜索應(yīng)用程序和電子商務(wù)站點(diǎn)等等而言,這非常適合。在以上任何一種 Web 應(yīng)用程序中,服務(wù)器提供用戶請(qǐng)求的數(shù)據(jù),然后關(guān)閉連接,并釋放那個(gè)線程,使之可以為其他請(qǐng)求服務(wù)。如果提供初始數(shù)據(jù)之后仍可能存在交互,那么將連接保持為打開狀態(tài),因此線程就不能釋放出來,服務(wù)器也就不能為很多用戶服務(wù)。
    但是,如果想在對(duì)請(qǐng)求做出響應(yīng)并發(fā)送初始數(shù)據(jù)之后,仍然保持與用戶的交互呢?在 Web 早期,這一點(diǎn)常使用 meta 刷新實(shí)現(xiàn)。這將自動(dòng)指示瀏覽器在指定秒數(shù)之后重新裝載頁(yè)面,從而支持簡(jiǎn)陋的輪詢(polling)。這不僅是一種糟糕的用戶體驗(yàn),而且通常效率非常低下。如果沒有新的數(shù)據(jù)要顯示在頁(yè)面上呢?這時(shí)不得不重新呈現(xiàn)同樣的頁(yè)面。如果對(duì)頁(yè)面的更改很少,并且頁(yè)面的大部分沒有變化呢?同樣,不管是否有必要,都得重新請(qǐng)求和獲取頁(yè)面上的一切內(nèi)容。
    Ajax 的發(fā)明和流行改變了上述狀況。現(xiàn)在,服務(wù)器可以異步通信,因此不必重新請(qǐng)求整個(gè)頁(yè)面?,F(xiàn)在可以進(jìn)行增量式的更新。只需使用 XMLHttpRequest 輪詢服務(wù)器。這項(xiàng)技術(shù)通常被稱作 Comet.這項(xiàng)技術(shù)存在一些變體,每種變體具有不同的性能和可伸縮性。我們來看看這些不同風(fēng)格的 Comet.
 
Comet 風(fēng)格
    Ajax 的出現(xiàn)使 Comet 成為可能。HTTP 的單向性質(zhì)可以有效地加以規(guī)避。實(shí)際上有一些不同的方法可以繞過這一點(diǎn)。您可能已經(jīng)猜到,支持 Comet 的最容易的方式是輪詢(poll)。使用 XMLHttpRequest 向服務(wù)器發(fā)出調(diào)用,返回后,等待一段固定的時(shí)間(通常使用 JavaScript 的 setTimeout 函數(shù)),然后再次調(diào)用。這是一項(xiàng)非常常見的技術(shù)。例如,大多數(shù) webmail 應(yīng)用程序就是通過這種技術(shù)在電子郵件到達(dá)時(shí)顯示電子郵件的。
    這項(xiàng)技術(shù)有優(yōu)點(diǎn)也有缺點(diǎn)。在這種情況下,您期望快速返回響應(yīng),就像任何其他 Ajax 請(qǐng)求一樣。在請(qǐng)求之間必須有一段暫停。否則,連續(xù)不斷的請(qǐng)求會(huì)沖垮服務(wù)器,并且這種情況下顯然不具有可伸縮性。這段暫停使應(yīng)用程序產(chǎn)生一個(gè)延時(shí)。暫停的時(shí)間越長(zhǎng),服務(wù)器上的新數(shù)據(jù)就需要越多的時(shí)間才能到達(dá)客戶機(jī)。如果縮短暫停時(shí)間,又將重新面臨沖垮服務(wù)器的風(fēng)險(xiǎn)。但是另一方面,這顯然是最簡(jiǎn)單的實(shí)現(xiàn) Comet 的方式。
    現(xiàn)在應(yīng)該指出,很多人認(rèn)為輪詢并不屬于 Comet.相反,他們認(rèn)為 Comet 是對(duì)輪詢的局限性的一個(gè)解決方案。最常見的 “真正的” Comet 技術(shù)是輪詢的一種變體,即長(zhǎng)輪詢(long polling)。輪詢與長(zhǎng)輪詢之間的主要區(qū)別在于服務(wù)器花多長(zhǎng)的時(shí)間作出響應(yīng)。長(zhǎng)輪詢通常將連接保持一段較長(zhǎng)的時(shí)間 — 通常是數(shù)秒鐘,但是也可能是一分鐘甚至更長(zhǎng)。當(dāng)服務(wù)器上發(fā)生某個(gè)事件時(shí),響應(yīng)被發(fā)送并隨即關(guān)閉,輪詢立即重新開始。
    長(zhǎng)輪詢相對(duì)于一般輪詢的優(yōu)點(diǎn)在于,數(shù)據(jù)一旦可用,便立即從服務(wù)器發(fā)送到客戶機(jī)。請(qǐng)求可能等待較長(zhǎng)的時(shí)間,期間沒有任何數(shù)據(jù)返回,但是一旦有了新的數(shù)據(jù),它將立即被發(fā)送到客戶機(jī)。因此沒有延時(shí)。如果您使用過基于 Web 的聊天程序,或者聲稱 “實(shí)時(shí)” 的任何程序,那么它很可能就是使用了這種技術(shù)。
    長(zhǎng)輪詢有一種變體,這是第三種風(fēng)格的 Comet.這通常被稱為流(streaming)。按照這種風(fēng)格,服務(wù)器將數(shù)據(jù)推回客戶機(jī),但是不關(guān)閉連接。連接將一直保持開啟,直到過期,并導(dǎo)致重新發(fā)出請(qǐng)求。XMLHttpRequest 規(guī)范表明,可以檢查 readyState 的值是否為 3 或 Receiving(而不是 4 或 Loaded),并獲取正從服務(wù)器 “流出” 的數(shù)據(jù)。和長(zhǎng)輪詢一樣,這種方式也沒有延時(shí)。當(dāng)服務(wù)器上的數(shù)據(jù)就緒時(shí),該數(shù)據(jù)被發(fā)送到客戶機(jī)。這種方式的另一個(gè)優(yōu)點(diǎn)是可以大大減少發(fā)送到服務(wù)器的請(qǐng)求,從而避免了與設(shè)置服務(wù)器連接相關(guān)的開銷和延時(shí)。不幸的是,XMLHttpRequest 在不同的瀏覽器中有很多不同的實(shí)現(xiàn)。這項(xiàng)技術(shù)只能在較新版本的 Mozilla Firefox 中可靠地使用。對(duì)于 Internet Explorer 或 Safari,仍需使用長(zhǎng)輪詢。
    至此,您可能會(huì)想,長(zhǎng)輪詢和流都有一個(gè)很大的問題。請(qǐng)求需要在服務(wù)器上存在一段較長(zhǎng)的時(shí)間。這打破了每個(gè)請(qǐng)求使用一個(gè)線程的模型,因?yàn)橛糜谝粋€(gè)請(qǐng)求的線程一直沒有被釋放。更糟糕的是,除非要發(fā)回?cái)?shù)據(jù),否則該線程一直處于空閑狀態(tài)。這顯然不具有可伸縮性。幸運(yùn)的是,現(xiàn)代 Java Web 服務(wù)器有很多方式可以解決這個(gè)問題。
    Java 中的 Comet
    現(xiàn)在有很多 Web 服務(wù)器是用 Java 構(gòu)建的。一個(gè)原因是 Java 有一個(gè)豐富的本地線程模型。因此實(shí)現(xiàn)典型的每個(gè)連接一個(gè)線程的模型便非常簡(jiǎn)單。該模型對(duì)于 Comet 不大適用,但是,Java 對(duì)此同樣有解決的辦法。為了有效地處理 Comet,需要非阻塞 IO,Java 通過它的 NIO 庫(kù)提供非阻塞 IO.兩種最流行的開源服務(wù)器 Apache Tomcat 和 Jetty 都利用 NIO 增加非阻塞 IO,從而支持 Comet.然而,這兩種服務(wù)器中的實(shí)現(xiàn)卻各不相同。我們來看看 Tomcat 和 Jetty 對(duì) Comet 的支持。
 
Tomcat 和 Comet
    對(duì)于 Apache Tomcat,要使用 Comet,主要需要做兩件事。首先,需要對(duì) Tomcat 的配置文件 server.XML 稍作修改。默認(rèn)情況下啟用的是更典型的同步 IO 連接器。現(xiàn)在只需將它切換成異步版本,如清單 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"/> 

 
    Servlet.這顯然是 Tomcat 特有的一個(gè)接口。清單 2 顯示了一個(gè)這樣的例子。
    清單 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 接口要求實(shí)現(xiàn) event 方法。這是用于 Comet 交互的一個(gè)生命周期方法。Tomcat 將使用不同的 CometEvent 實(shí)例調(diào)用。通過檢查 CometEvent 的 eventType,可以判斷正處在生命周期的哪個(gè)階段。當(dāng)請(qǐng)求第一次傳入時(shí),即發(fā)生 BEGIN 事件。READ 事件表明數(shù)據(jù)正在被發(fā)送,只有當(dāng)請(qǐng)求為 POST 時(shí)才需要該事件。遇到 END 或 ERROR 事件時(shí),請(qǐng)求終止。

在清單 2 的例子中,Servlet 使用一個(gè) MessageSender 類發(fā)送數(shù)據(jù)。這個(gè)類的實(shí)例是在 servlet 的 init 方法中在其自身的線程中創(chuàng)建,并在 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); 
  }
  }
  }
  } 


這個(gè)類基本上是樣板代碼,與 Comet 沒有直接的關(guān)系。但是,有兩點(diǎn)要注意。這個(gè)類含有一個(gè) ServletResponse 對(duì)象?;仡^看看清單 2 中的 event 方法,當(dāng)事件為 BEGIN 時(shí),response 對(duì)象被傳入到 MessageSender 中。在 MessageSender 的 run 方法中,它使用 ServletResponse 將數(shù)據(jù)發(fā)送回客戶機(jī)。注意,一旦發(fā)送完所有排隊(duì)等待的消息后,它將關(guān)閉連接。這樣就實(shí)現(xiàn)了長(zhǎng)輪詢。如果要實(shí)現(xiàn)流風(fēng)格的 Comet,那么需要使連接保持開啟,但是仍然刷新數(shù)據(jù)。

回頭看清單 2 可以發(fā)現(xiàn),其中創(chuàng)建了一個(gè) Weatherman 類。正是這個(gè)類使用 MessageSender 將數(shù)據(jù)發(fā)送回客戶機(jī)。這個(gè)類使用 Yahoo RSS feed 獲得不同地區(qū)的天氣信息,并將該信息發(fā)送到客戶機(jī)。這是一個(gè)特別設(shè)計(jì)的例子,用于模擬以異步方式發(fā)送數(shù)據(jù)的數(shù)據(jù)源。清單 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(); 
  }
  } 


這個(gè)類使用 Project Rome 庫(kù)解析來自 Yahoo Weather 的 RSS feed.如果需要生成或使用 RSS 或 Atom feed,這是一個(gè)非常有用的庫(kù)。此外,這個(gè)代碼中只有一個(gè)地方值得注意,那就是它產(chǎn)生另一個(gè)線程,用于每過 30 秒鐘發(fā)送一次天氣數(shù)據(jù)。最后,我們?cè)倏匆粋€(gè)地方:使用該 Servlet 的客戶機(jī)代碼。在這種情況下,一個(gè)簡(jiǎn)單的 JSP 加上少量的 JavaScript 就足夠了。清單 5 顯示了該代碼。

清單 5. 客戶機(jī) Comet 代碼
 

  "http://www.w3.org/TR/html4/loose.dtd"> 
 
 
   
     
     
            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);  
      } 

該代碼只是在用戶單擊 Go 按鈕時(shí)開始長(zhǎng)輪詢。注意,它直接使用 XMLHttpRequest 對(duì)象,所以這在 Internet Explorer 6 中將不能工作。您可能需要使用一個(gè) Ajax 庫(kù)解決瀏覽器差異問題。除此之外,惟一需要注意的是回調(diào)函數(shù),或者為請(qǐng)求的 onreadystatechange 函數(shù)創(chuàng)建的閉包。該函數(shù)粘貼來自服務(wù)器的新的數(shù)據(jù),然后重新調(diào)用 go 函數(shù)。

現(xiàn)在,我們看過了一個(gè)簡(jiǎn)單的 Comet 應(yīng)用程序在 Tomcat 上是什么樣的。有兩件與 Tomcat 密切相關(guān)的事情要做:一是配置它的連接器,二是在 Servlet 中實(shí)現(xiàn)一個(gè)特定于 Tomcat 的接口。您可能想知道,將該代碼 “移植” 到 Jetty 有多大難度。接下來我們就來看看這個(gè)問題。
Jetty 和 Comet

Jetty 服務(wù)器使用稍微不同的技術(shù)來支持 Comet 的可伸縮的實(shí)現(xiàn)。Jetty 支持被稱作 continuations 的編程結(jié)構(gòu)。其思想很簡(jiǎn)單。請(qǐng)求先被暫停,然后在將來的某個(gè)時(shí)間點(diǎn)再繼續(xù)。規(guī)定時(shí)間到期,或者某種有意義的事件發(fā)生,都可能導(dǎo)致請(qǐng)求繼續(xù)。當(dāng)請(qǐng)求被暫停時(shí),它的線程被釋放。

可以使用 Jetty 的 org.mortbay.util.ajax.ContinuationSupport 類為任何 HttpServletRequest 創(chuàng)建 org.mortbay.util.ajax.Continuation 的一個(gè)實(shí)例。這種方法與 Comet 有很大的不同。但是,continuations 可用于實(shí)現(xiàn)邏輯上等效的 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)); 
  }
  } 


這里最需要注意的是,該結(jié)構(gòu)與 Tomcat 版本的代碼非常類似。begin、read、end 和 error 方法都與 Tomcat 中相同的事件匹配。該 Servlet 的 service 方法被覆蓋為在請(qǐng)求第一次進(jìn)入時(shí)創(chuàng)建一個(gè) continuation 并暫停該請(qǐng)求,直到超時(shí)時(shí)間已到,或者發(fā)生導(dǎo)致它重新開始的事件。上面沒有顯示 init 和 destroy 方法,因?yàn)樗鼈兣c Tomcat 版本是一樣的。該 servlet 使用與 Tomcat 相同的 MessageSender.因此不需要修改。注意 begin 方法如何創(chuàng)建 Weatherman 實(shí)例。對(duì)這個(gè)類的使用與 Tomcat 版本中也是完全相同的。甚至客戶機(jī)代碼也是一樣的。只有 servlet 有更改。雖然 servlet 的變化比較大,但是與 Tomcat 中的事件模型仍是一一對(duì)應(yīng)的。

希望這足以鼓舞人心。雖然完全相同的代碼不能同時(shí)在 Tomcat 和 Jetty 中運(yùn)行,但是它是非常相似的。當(dāng)然,JavaEE 吸引人的一點(diǎn)是可移植性。大多數(shù)在 Tomcat 中運(yùn)行的代碼,無需修改就可以在 Jetty 中運(yùn)行,反之亦然。因此,毫不奇怪,下一個(gè)版本的 Java Servlet 規(guī)范包括異步請(qǐng)求處理(即 Comet 背后的底層技術(shù))的標(biāo)準(zhǔn)化。 我們來看看這個(gè)規(guī)范:Servlet 3.0 規(guī)范。
Servlet 3.0 規(guī)范

在此,我們不深究 Servlet 3.0 規(guī)范的全部細(xì)節(jié),只看看 Comet servlet 如果在 Servlet 3.0 容器中運(yùn)行,可能會(huì)是什么樣子。注意 “可能” 二字。該規(guī)范已經(jīng)發(fā)布公共預(yù)覽版,但在撰寫本文之際,還沒有最終版。因此,清單 7 顯示的是遵從公共預(yù)覽規(guī)范的一個(gè)實(shí)現(xiàn)。

清單 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); ; 
  }
  } 


值得高興的是,這個(gè)版本要簡(jiǎn)單得多。平心而論,如果不遵從 Tomcat 的事件模型,在 Jetty 中可以有類似的實(shí)現(xiàn)。這種事件模型似乎比較合理,很容易在 Tomcat 以外的容器(例如 Jetty)中實(shí)現(xiàn),只是沒有相關(guān)的標(biāo)準(zhǔn)。

回頭看看清單 7,注意它的標(biāo)注聲明它支持異步處理,并設(shè)置了超時(shí)時(shí)間。startAsync 方法是 HttpServletRequest 上的一個(gè)新方法,它返回新的 javax.servlet.AsyncContext 類的一個(gè)實(shí)例。注意,MessageSender 現(xiàn)在傳遞 AsynContext 的引用,而不是 ServletResponse 的引用。在這里,不應(yīng)該關(guān)閉響應(yīng),而是調(diào)用 AsyncContext 實(shí)例上的 complete 方法。還應(yīng)注意,Weatherman 被直接傳遞到 AsyncContext 實(shí)例的 start 方法。這樣將在當(dāng)前 ServletContext 中開始一個(gè)新線程。

而且,盡管與 Tomcat 或 Jetty 相比都有較大的不同,但是修改相同風(fēng)格的編程來處理 Servlet 3.0 規(guī)范提議的 API 并不是太難。還應(yīng)注意,Jetty 7 是為實(shí)現(xiàn) Servlet 3.0 而設(shè)計(jì)的,目前處于 beta 狀態(tài)。但是,在撰寫本文之際,它還沒有實(shí)現(xiàn)該規(guī)范的最新版本。

結(jié)束語

Comet 風(fēng)格的 Web 應(yīng)用程序可以為 Web 帶來全新的交互性。它為大規(guī)模地實(shí)現(xiàn)這些特性帶來一些復(fù)雜的挑戰(zhàn)。但是,領(lǐng)先的 Java Web 服務(wù)器正在為實(shí)現(xiàn) Comet 提供成熟、穩(wěn)定的技術(shù)。在本文中,您看到了 Tomcat 和 Jetty 上當(dāng)前風(fēng)格的 Comet 的不同點(diǎn)和相似點(diǎn),以及正在進(jìn)行的 Servlet 3.0 規(guī)范的標(biāo)準(zhǔn)化。Tomcat 和 Jetty 使如今構(gòu)建可伸縮的 Comet 應(yīng)用程序成為可能,并且明確了未來面向 Servlet 3.0 標(biāo)準(zhǔn)化的升級(jí)路線。

相關(guān)文章

  • Java自定義equals產(chǎn)生的問題分析

    Java自定義equals產(chǎn)生的問題分析

    這篇文章主要介紹了Java自定義equals時(shí)super.equals帶來的問題分析,總的來說這并不是一道難題,那為什么要拿出這道題介紹?拿出這道題真正想要傳達(dá)的是解題的思路,以及不斷優(yōu)化探尋最優(yōu)解的過程。希望通過這道題能給你帶來一種解題優(yōu)化的思路
    2023-01-01
  • Spring Boot 啟動(dòng)端口如何啟動(dòng)

    Spring Boot 啟動(dòng)端口如何啟動(dòng)

    這篇文章主要介紹了Spring Boot 啟動(dòng)端口如何啟動(dòng)的相關(guān)資料,需要的朋友可以參考下
    2016-12-12
  • java使用TimerTask定時(shí)器獲取指定網(wǎng)絡(luò)數(shù)據(jù)

    java使用TimerTask定時(shí)器獲取指定網(wǎng)絡(luò)數(shù)據(jù)

    java.util.Timer定時(shí)器,實(shí)際上是個(gè)線程,定時(shí)調(diào)度所擁有的TimerTasks。一個(gè)TimerTask實(shí)際上就是一個(gè)擁有run方法的類,需要定時(shí)執(zhí)行的代碼放到run方法體內(nèi),TimerTask一般是以匿名類的方式創(chuàng)建,下面的就用示例來學(xué)習(xí)他的使用方法
    2014-01-01
  • JDK環(huán)境變量配置的具體操作步驟

    JDK環(huán)境變量配置的具體操作步驟

    本篇文章介紹了,JDK環(huán)境變量配置的具體操作步驟。需要的朋友參考下
    2013-05-05
  • 關(guān)于Spring?Boot項(xiàng)目的?log4j2?核彈漏洞問題(一行代碼配置搞定)

    關(guān)于Spring?Boot項(xiàng)目的?log4j2?核彈漏洞問題(一行代碼配置搞定)

    相信昨天,很多小伙伴都因?yàn)長(zhǎng)og4j2的史詩(shī)級(jí)漏洞忙翻了吧,不過我看到群里發(fā)出來的各種修復(fù)方法,還真是不好看...所以這里也提一下Spring Boot用戶怎么修復(fù)最簡(jiǎn)單吧,對(duì)Spring Boot log4j2 核彈漏洞問題感興趣的朋友參考下吧
    2021-12-12
  • java加密MD5實(shí)現(xiàn)及密碼驗(yàn)證代碼實(shí)例

    java加密MD5實(shí)現(xiàn)及密碼驗(yàn)證代碼實(shí)例

    這篇文章主要介紹了java加密MD5實(shí)現(xiàn)及密碼驗(yàn)證代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12
  • mybatis-generator生成文件覆蓋問題的解決

    mybatis-generator生成文件覆蓋問題的解決

    這篇文章主要介紹了mybatis-generator生成文件覆蓋問題的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • 詳細(xì)聊聊Spring MVC重定向與轉(zhuǎn)發(fā)

    詳細(xì)聊聊Spring MVC重定向與轉(zhuǎn)發(fā)

    大家應(yīng)該都知道請(qǐng)求重定向和請(qǐng)求轉(zhuǎn)發(fā)都是web開發(fā)中資源跳轉(zhuǎn)的方式,這篇文章主要給大家介紹了關(guān)于Spring MVC重定向與轉(zhuǎn)發(fā)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2021-09-09
  • 教你用Java Swing實(shí)現(xiàn)自助取款機(jī)系統(tǒng)

    教你用Java Swing實(shí)現(xiàn)自助取款機(jī)系統(tǒng)

    今天給大家?guī)淼氖顷P(guān)于JAVA的相關(guān)知識(shí),文章圍繞著如何用Java Swing實(shí)現(xiàn)自助取款機(jī)系統(tǒng)展開,文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下
    2021-06-06
  • SpringBoot項(xiàng)目打包war包時(shí)無法運(yùn)行問題的解決方式

    SpringBoot項(xiàng)目打包war包時(shí)無法運(yùn)行問題的解決方式

    在開發(fā)工程中,使用啟動(dòng)類啟動(dòng)能夠正常啟動(dòng)并測(cè)試,下面這篇文章主要給大家介紹了關(guān)于SpringBoot項(xiàng)目打包war包時(shí)無法運(yùn)行問題的解決方式,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-06-06

最新評(píng)論