基于dwr2.0的Push推送技術詳細解析以及實例

jopen 11年前發布 | 37K 次閱讀 DWR Ajax框架

DWR從2.0開始增加了push功能,也就是在異步傳輸的情況下可以從Web-Server端發送數據到
Browser.
我們知道,Web的訪問機制天生是設計用來pull數據的,也就是只允許Browser端主動發起請求,server
是被動的響應.不允許Server向Browser發出一個connection請求,也就是說沒有為server向Browser
push數據提供設計實現.
雖然沒有直接的實現方法,卻可以使用一些變通的方式完成類似的功能:
1. Polling
Polling其實就是輪詢,是通過Browser在一個相對短的間隔時間內,反復向Server發出請求,然
后更新頁面,這種方式沒有什么新鮮的,只是需要瀏覽器端做一些工作就可以,哪怕沒有太多服務器端的配
置也沒問題.輪詢的方式對于服務器來說會依據不同的訪問間隔而產生不同程度的額外負載,因為每次訪
問都有重新建立連接的過程.
2. Comet
Comet方式通俗的說就是一種長連接機制(long lived http).同樣是由Browser端主動發起請
求,但是Server端以一種似乎非常慢的響應方式給出回答,這樣在這個期間內,服務器端可以使用同一個
connection把要更新的數據主動發送給Browser.Comet又有很多中實現方式,但是總的來說對Server
端的負載都會有增加.雖然對于單位操作來說,每次只需要建議一次connection,但是由于connection是
保持較長時間的,對于server端的資源的占用要有所增加.
3. Piggyback
Piggyback方式是一種半主動的方式,也就是說還是由Browser主動發出請求,但是每次請求的
響應中除了當次的響應之外,還會把上次請求以來已經發生的變化同時發給Browser.也就是說,當次請
求的更新會搭載到下一次請求的響應中一并發回.這樣,在Browser的感覺就好象上一次請求又有了更
新.但是這種感覺取決于Browser向Server發出請求的頻度.如果,第二次請求遲遲沒有發出,那么上一次
的更新就不會取到.
在DWR2.0中可以使用Active(主動) 和 Passive(被動)兩種工作模式,在這里我們主要討論
Active(主動)模式.Active(主動)模式又分為以下3種:
? Full Streaming Mode
? Early Closing Mode
? Polling Mode
Full Streaming Mode
這是Active模式下的一種默認配置,具有很快的響應速度,而且建立好的鏈接只有每60秒檢查一次瀏
覽器是否是活躍的.這種工作模式的配置非常簡單,在Web.xml中配置DWR的時候,加上下面的內容:

<servlet>
<servlet-name>dwr-invoker</servlet-name>
<servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
<init-param>
<param-name>activeReverseAjaxEnabled</param-name>
<param-value>true</param-value>
</init-param>
</servlet>

然后在Browser頁面端加上下面一句就可以了:
dwr.engine.setActiveReverseAjax(true);
需要說明的是,長鏈接會增加Server的資源占用,有些Server比如Jetty允許在客戶端關閉線程
(connection),在新版本中會把這種能力延伸到GlassFish 和Tomcat.總之,DWR的主導思想是盡
量保護Server,減小負載.
Early Closing Mode
在Browser和Server之間有Proxy或者mod_jk的情況下,需要能夠良好的工作,需要這種模式:這種
模式和Full Streaming Mode相似,以Full模式開啟connection,但是,如果沒有輸出的情況下,
它會在一個配置好的時間內關閉Connection,通常這個時間是60秒.
從2.04版開始,DWR默認使用Early Closing Mode,如果要要想使用Full Streaming Mode,需
要進行如下的配置:

<init-param>
<param-name>maxWaitAfterWrite</param-name>
<param-value>-1</param-value>
</init-param>

這里,設置maxWaitAfterWrite是-1,表示這個時間和Full Streaming Mode一樣,設置關閉時間是60
秒.
Polling Mode
Polling Mode 是一種輪詢方式,這可以避免長時間保持連接而產生的對服務器資源的占用.如果要是用
輪詢方式,還需要做以下的配置:

<init-param>
<param-name>org.directwebremoting.extend.ServerLoadMonitor</param-name>
<param-value>org.directwebremoting.impl.PollingServerLoadMonitor</param-value>
</init-param>
<init-param>
<param-name>disconnectedTime</param-name>
<param-value>60000</param-value>
</init-param>
這是將輪詢周期改為6000毫秒,也就是6秒

讓Web具備了Push的方式,這對于很多應用是夢寐以求的,比如,如果有一個基于Web的網絡聊天系統,
如果使用Push技術可以更加滿足功能的需要,還有比如說一些需要server端根據數據條件主動向
browser端發送數據的應用需求,都非常需要這樣的功能.
下面就舉一個股票報盤的例子,能夠讓Server端通過主動的方式想Browser端發送股票信息.

先說一下所需jar包:dwr.jar commons-logging.jar
然后介紹如何配置:

  1. 在web.xml中配置如下內容: </p>
    <servlet>
    <servlet-name>dwr-invoker</servlet-name>
    <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
    <init-param>
    <param-name>activeReverseAjaxEnabled</param-name>
    <param-value>true</param-value>
    </init-param>
    </servlet>
    <servlet-mapping>
    <servlet-name>dwr-invoker</servlet-name>
    <url-pattern>/dwr/*</url-pattern>
    </servlet-mapping>

  2. 在dwr.xml中配置如下內容:
    <dwr>
    <allow>
    <!-- Reverse Ajax Stock push Demo Config -->
    <create creator="new" javascript="StocksPusher">
    <param name="class" value="dwr.reverse.StocksPusher"/>
    </create>
    </allow>
    </dwr>

  3. 股票報盤的頁面getStockInfo.html
    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    ";
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>DWR Reverse Ajax Demo : Show Stock info</title>
    <link rel="stylesheet" type="text/css" href="generic.css" />
    <script type='text/javascript' src='/AjaxShow/dwr/engine.js'></script>
    <script type='text/javascript' src='/AjaxShow/dwr/util.js'></script>
    <script type='text/javascript' src='/AjaxShow/dwr/interface/
    StocksPusher.js'></script>
    <script type="text/javascript">
    function beginShow() {
    StocksPusher.beginShow();
    StocksPusher.sendStocks();
    }
    function endShow(){
    StocksPusher.closeShow();
    }
    </script>
    </head>
    <body onload="dwr.engine.setActiveReverseAjax(true);">
    <h3>使用DWR Reverse Ajax進行股票報盤</h3>
    <p>下面顯示的股票信息是可以動態變化的</p>

    <input type="button" value="開市..." onclick="beginShow()"/>

    <input type="button" value="閉市..." onclick="endShow()"/> <hr> <table style="width:500px" border="0" cellpadding="0"> <tr> <td class="headName" ><b>Stock Name</b></td> <td class="headValue" ><b>Stock Value</b></td> </tr> <tr><td>中移動</td><td><div id="zyd">wait...</div></td></tr> <tr><td>中石化</td><td><div id="zsh">wait...</div></td></tr> <tr><td>中石油</td><td><div id="zsy">wait...</div></td></tr> <tr><td>海爾電器</td><td><div id="hedq">wait...</div></td></tr> <tr><td>冀東水泥</td><td><div id="jdsn">wait...</div></td></tr> <tr><td>用友軟件</td><td><div id="yyrj">wait...</div></td></tr> <tr><td>柳鋼股份</td><td><div id="lggf">wait...</div></td></tr> <tr><td>招商銀行</td><td><div id="zsyh">wait...</div></td></tr> <tr><td>中國鐵建</td><td><div id="zgtj">wait...</div></td></tr> <tr><td>深發展</td><td><div id="sfz">wait...</div></td></tr> <tr><td>金山軟件</td><td><div id="jsrj">wait...</div></td></tr> <tr><td>大連實德</td><td><div id="dlsd">wait...</div></td></tr> <tr><td>九寨溝</td><td><div id="jzg">wait...</div></td></tr> <tr><td>中國平安</td><td><div id="zgpa">wait...</div></td></tr> <tr><td>工商銀行</td><td><div id="gsyh">wait...</div></td></tr> <tr><td>鞍鋼股份</td><td><div id="aggf">wait...</div></td></tr> <tr><td>中國航天</td><td><div id="zght">wait...</div></td></tr> </table> <br> </body> </html></pre>
  4. 報盤的主程序StocksPusher.java ,關鍵部分在代碼后面有中文注釋
    package dwr.reverse;
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.List;
    import org.directwebremoting.WebContext;
    import org.directwebremoting.WebContextFactory;
    import org.directwebremoting.proxy.dwr.Util;
    import org.directwebremoting.util.Logger;
    /**
  5. Reverse Ajax class. *
  6. @author Henry Huang */ public class StocksPusher { private static boolean closeMarket = false; /**
  7. Initialize the stocklist with values. */ public StocksPusher() { } /**
  8. Send the Stock-Values to the file "getStockInfo.html" */ public void sendStocks() throws InterruptedException { WebContext wctx = WebContextFactory.get(); //這里是獲取WebContext上下文 String currentPage = wctx.getCurrentPage(); //從上下文中獲取當前頁面,這些是DWR Reverse Ajax 要求的必須方式 Collection sessions = wctx.getScriptSessionsByPage(currentPage); //再一個page中 可能存在多個ScriptSessions, Util utilAll = new Util(sessions); //Util 是DWR 在Server端模擬Brower端 dwr.util.js 的類, Engine也是 while(true){ Thread.sleep(500); if(closeMarket) break; StocksBean stock = StockPriceTracer.getNextStockInfo(); utilAll.setValue(stock.getStock(), stock.getValue()); //這里的setValue()用法和 dwr.util.js中的setValue()函數用法完全一樣,第一個參數是頁面Element的id ,第二個參數是對該id 賦的新值 System.out.println("Pushing stock: " + stock.getStock() + " = " + stock.getValue()); } } public void beginShow(){ closeMarket = false; } public void closeShow(){ closeMarket = true; } }</pre>
  9. 還有一個類是為了模擬實時獲取股票信息的工具StockPriceTracer.java,也可能是訪問數據庫,
    也可能來至衛星的大盤數據,等等,這個類是用隨機的方法獲得股票價格:
    package dwr.reverse;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Random;
    import java.util.Stack;
    /**
  10. Reverse Ajax class. *
  11. @author Henry Huang */ public class StockPriceTracer { private static StockPriceTracer tracer = null; private List<StocksBean> stocks = new ArrayList<StocksBean>(); private Stack<StocksBean> cycleStack = new Stack<StocksBean>(); private StockPriceTracer(){ stocks.add(new StocksBean("zsy", "36.55")); stocks.add(new StocksBean("dlsd", "91.01")); stocks.add(new StocksBean("zsh", "22.59")); stocks.add(new StocksBean("lggf", "5.07")); stocks.add(new StocksBean("hedq", "71.77")); stocks.add(new StocksBean("jdsn", "31.61")); stocks.add(new StocksBean("yyrj", "51.29")); stocks.add(new StocksBean("zsyh", "52.70")); stocks.add(new StocksBean("zgtj", "16.96")); stocks.add(new StocksBean("sfz", "54.34")); stocks.add(new StocksBean("jsrj", "178.48")); stocks.add(new StocksBean("zyd", "134.48")); stocks.add(new StocksBean("jzg", "76.32")); stocks.add(new StocksBean("zgpa", "80.63")); stocks.add(new StocksBean("gsyh", "18.79")); stocks.add(new StocksBean("aggf", "20.19")); stocks.add(new StocksBean("zght", "11.13")); } public static StocksBean getNextStockInfo(){ if(null == tracer) tracer = new StockPriceTracer(); if(tracer.cycleStack.empty()) tracer.cycleStack.addAll(tracer.stocks); StocksBean tmp = tracer.cycleStack.pop(); tmp.setValue(tracer.getRandomPrice(tmp.getValue())); return tmp; } private String getRandomPrice(String current){ float fcurrent = 0.0F; try{ fcurrent = Float.parseFloat(current); }catch(NumberFormatException e){ fcurrent = 0.01F; } Random rdm = new Random(); float tmp = fcurrent + rdm.nextFloat(); return String.valueOf(tmp); } }</pre>
  12. 還有一個類是一個JavaBeanStockBean.java
    package dwr.reverse;
    public class StocksBean {
    private String stock = "";
    private String value = "";
    public StocksBean(String stock, String value) {
    this.setStock(stock);
    this.setValue(value);
    }
    public String getStock() {
    return stock;
    }
    public void setStock(String stock) {
    this.stock = stock;
    }
    public String getValue() {
    return value;
    }
    public void setValue(String value) {
    this.value = value;
    }
    }
    來自:
    http://my.oschina.net/barter/blog/120435
 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!