熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java核心技術 >> 正文

使用Java實現Comet風格的Web應用(一)

2013-11-23 19:23:35  來源: Java核心技術 

  開始

  在本文中我將展示如何使用各種不同的 Java 技術構建一些簡單的 Comet 風格的 Web 應用程序讀者對 Java ServletAjax 和 JavaScript 應該有一定的了解我們將考察 Tomcat 和 Jetty 中一些支持 Comet 的特性因此需要使用這兩個產品的最新版本本文使用 Tomcat 和 Jetty 另外還需要一個支持 Java 或更高版本的 JDK本文使用 JDK 此外還需要看看 Jetty 的預發布版因為它實現了 Servlet 規范我們將在本文中研究該規范

  理解 Comet

  您可能已經聽說過 Comet因為它最近受到了一定的關注Comet 有時也稱反向 Ajax 或服務器端推技術(serverside 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 的值是否為 或 Receiving(而不是 或 Loaded)並獲取正從服務器 流出 的數據和長輪詢一樣這種方式也沒有延時當服務器上的數據就緒時該數據被發送到客戶機這種方式的另一個優點是可以大大減少發送到服務器的請求從而避免了與設置服務器連接相關的開銷和延時不幸的是XMLHttpRequest 在不同的浏覽器中有很多不同的實現這項技術只能在較新版本的 Mozilla Firefox 中可靠地使用對於 Internet Explorer 或 Safari仍需使用長輪詢

  至此您可能會想長輪詢和流都有一個很大的問題請求需要在服務器上存在一段較長的時間這打破了每個請求使用一個線程的模型因為用於一個請求的線程一直沒有被釋放更糟糕的是除非要發回數據否則該線程一直處於空閒狀態這顯然不具有可伸縮性幸運的是現代 Java Web 服務器有很多方式可以解決這個問題

  Java 中的 Comet

  現在有很多 Web 服務器是用 Java 構建的一個原因是 Java 有一個豐富的本地線程模型因此實現典型的每個連接一個線程的模型便非常簡單該模型對於 Comet 不大適用但是Java 對此同樣有解決的辦法為了有效地處理 Comet需要非阻塞 IOJava 通過它的 NIO 庫提供非阻塞 IO兩種最流行的開源服務器 Apache Tomcat 和 Jetty 都利用 NIO 增加非阻塞 IO從而支持 Comet然而這兩種服務器中的實現卻各不相同我們來看看 Tomcat 和 Jetty 對 Comet 的支持

  Tomcat 和 Comet

  對於 Apache Tomcat要使用 Comet主要需要做兩件事首先需要對 Tomcat 的配置文件 serverXML 稍作修改默認情況下啟用的是更典型的同步 IO 連接器現在只需將它切換成異步版本如清單 所示

  清單 修改 Tomcat 的 serverxml
 <! This is the usual Connector comment it out and add the NIO one > 
  <! Connector URIEncoding=utf connectionTimeout= port= 
protocol=HTTP/ redirectPort=/ > 
<Connector connectionTimeout= port= protocol=orgapache 
coyote redirectPort=/> 

  Servlet這顯然是 Tomcat 特有的一個接口清單 顯示了一個這樣的例子

  清單 Tomcat Comet servlet
 public class TomcatWeatherServlet extends HttpServlet implements CometProcessor {
  private MessageSender messageSender = null;
  private static final Integer TIMEOUT =  * ;
  @Override
  public void destroy() {
  messageSenderstop();
  messageSender = null;
  }
  @Override
  public void init() throws ServletException {
  messageSender = new MessageSender();
  Thread messageSenderThread =
  new Thread(messageSender MessageSender[ + getServletContext()
  getContextPath() + ]);
  messageSenderThreadsetDaemon(true);
  messageSenderThreadstart();
  }
  public void event(final CometEvent event) throws IOException ServletException {
  HttpServletRequest request = eventgetHttpServletRequest();
  HttpServletResponse response = eventgetHttpServletResponse();
  if (eventgetEventType() == CometEventEventTypeBEGIN) {
  requestsetAttribute(oettimeout TIMEOUT);
  log(Begin for session:  + requestgetSession(true)getId());
  messageSendersetConnection(response);
  Weatherman weatherman = new Weatherman( );
  new Thread(weatherman)start();
  } else if (eventgetEventType() == CometEventEventTypeERROR) {
  log(Error for session:  + requestgetSession(true)getId());
  eventclose();
  } else if (eventgetEventType() == CometEventEventTypeEND) {
  log(End for session:  + requestgetSession(true)getId());
  eventclose();
  } else if (eventgetEventType() == CometEventEventTypeREAD) {
  throw new UnsupportedOperationException(This servlet does not accept
  data);
  }
  }
  }


From:http://tw.wingwit.com/Article/program/Java/hx/201311/26770.html
  • 上一篇文章:

  • 下一篇文章:
  • Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.