程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 應用Java完成相似Comet作風的web app

應用Java完成相似Comet作風的web app

編輯:關於JAVA

應用Java完成相似Comet作風的web app。本站提示廣大學習愛好者:(應用Java完成相似Comet作風的web app)文章只能為提供參考,不一定能成為您想要的結果。以下是應用Java完成相似Comet作風的web app正文


開端
    在本文中,我將展現若何應用各類分歧的 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"/> 

 
    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 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 代碼
 

  "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 按鈕時開端長輪詢。留意,它直接應用 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 尺度化的進級道路。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved