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

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的時候,加上下面的內容: javascript

<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端發送股票信息. css

先說一下所需jar包:dwr.jar commons-logging.jar
而後介紹如何配置:
1. 在web.xml中配置以下內容: html

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

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;
/**
* Reverse Ajax class.
*
* @author Henry Huang
*/
public class StocksPusher {
private static boolean closeMarket = false;
/**
* Initialize the stocklist with values.
*/
public StocksPusher() {
}
/**
* 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;
}
}

5. 還有一個類是爲了模擬實時獲取股票信息的工具StockPriceTracer.java,也多是訪問數據庫,
也可能來至衛星的大盤數據,等等,這個類是用隨機的方法得到股票價格:
package dwr.reverse;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Stack;
/**
* Reverse Ajax class.
*
* @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);
}
}

6. 還有一個類是一個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;
}
}
相關文章
相關標籤/搜索