基于dwr2.0的Push推送技术详细解析以及实例

来源:互联网 发布:fanuc 电池 数据全清 编辑:程序博客网 时间:2024/05/19 12:17
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的时候,加上下面的内容:
view source
print?
1<servlet>
2<servlet-name>dwr-invoker</servlet-name>
3<servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
4<init-param>
5<param-name>activeReverseAjaxEnabled</param-name>
6<param-value>true</param-value>
7</init-param>
8</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,需
要进行如下的配置:
view source
print?
1<init-param>
2<param-name>maxWaitAfterWrite</param-name>
3<param-value>-1</param-value>
4</init-param>

这里,设置maxWaitAfterWrite是-1,表示这个时间和Full Streaming Mode一样,设置关闭时间是60
秒.
Polling Mode
Polling Mode 是一种轮询方式,这可以避免长时间保持连接而产生的对服务器资源的占用.如果要是用
轮询方式,还需要做以下的配置:
view source
print?
1<init-param>
2<param-name>org.directwebremoting.extend.ServerLoadMonitor</param-name>
3<param-value>org.directwebremoting.impl.PollingServerLoadMonitor</param-value>
4</init-param>
view source
print?
1<init-param>
2<param-name>disconnectedTime</param-name>
3<param-value>60000</param-value>
4</init-param>
这是将轮询周期改为6000毫秒,也就是6秒

让Web具备了Push的方式,这对于很多应用是梦寐以求的,比如,如果有一个基于Web的网络聊天系统,
如果使用Push技术可以更加满足功能的需要,还有比如说一些需要server端根据数据条件主动向
browser端发送数据的应用需求,都非常需要这样的功能.
下面就举一个股票报盘的例子,能够让Server端通过主动的方式想Browser端发送股票信息.

先说一下所需jar包:dwr.jarcommons-logging.jar
然后介绍如何配置:
1. 在web.xml中配置如下内容:

view source
print?
01<servlet>
02<servlet-name>dwr-invoker</servlet-name>
03<servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
04<init-param>
05<param-name>activeReverseAjaxEnabled</param-name>
06<param-value>true</param-value>
07</init-param>
08</servlet>
09<servlet-mapping>
10<servlet-name>dwr-invoker</servlet-name>
11<url-pattern>/dwr/*</url-pattern>
12</servlet-mapping>

2. 在dwr.xml中配置如下内容:
view source
print?
1<dwr>
2<allow>
3<!-- Reverse Ajax Stock push Demo Config -->
4<createcreator="new"javascript="StocksPusher">
5<paramname="class"value="dwr.reverse.StocksPusher"/>
6</create>
7</allow>
8</dwr>

3. 股票报盘的页面getStockInfo.html
view source
print?
01<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
02"http://www.w3.org/TR/html4/loose.dtd">
03<html>
04<head>
05<metahttp-equiv="Content-Type"content="text/html; charset=UTF-8"/>
06<title>DWR Reverse Ajax Demo : Show Stock info</title>
07<linkrel="stylesheet"type="text/css"href="generic.css"/>
08<scripttype='text/javascript'src='/AjaxShow/dwr/engine.js'></script>
09<scripttype='text/javascript'src='/AjaxShow/dwr/util.js'></script>
10<scripttype='text/javascript'src='/AjaxShow/dwr/interface/
11StocksPusher.js'></script>
12<scripttype="text/javascript">
13function beginShow() {
14StocksPusher.beginShow();
15StocksPusher.sendStocks();
16}
17function endShow(){
18StocksPusher.closeShow();
19}
20</script>
21</head>
22<bodyonload="dwr.engine.setActiveReverseAjax(true);">
23<h3>使用DWR Reverse Ajax进行股票报盘</h3>
24<p>下面显示的股票信息是可以动态变化的</p>
25<inputtype="button"value="开市..."onclick="beginShow()"/>
26=========================
27<inputtype="button"value="闭市..."onclick="endShow()"/>
28<hr>
29<tablestyle="width:500px"border="0"cellpadding="0">
30<tr>
31<tdclass="headName"><b>Stock Name</b></td>
32<tdclass="headValue"><b>Stock Value</b></td>
33</tr>
34<tr><td>中移动</td><td><divid="zyd">wait...</div></td></tr>
35<tr><td>中石化</td><td><divid="zsh">wait...</div></td></tr>
36<tr><td>中石油</td><td><divid="zsy">wait...</div></td></tr>
37<tr><td>海尔电器</td><td><divid="hedq">wait...</div></td></tr>
38<tr><td>冀东水泥</td><td><divid="jdsn">wait...</div></td></tr>
39<tr><td>用友软件</td><td><divid="yyrj">wait...</div></td></tr>
40<tr><td>柳钢股份</td><td><divid="lggf">wait...</div></td></tr>
41<tr><td>招商银行</td><td><divid="zsyh">wait...</div></td></tr>
42<tr><td>中国铁建</td><td><divid="zgtj">wait...</div></td></tr>
43<tr><td>深发展</td><td><divid="sfz">wait...</div></td></tr>
44<tr><td>金山软件</td><td><divid="jsrj">wait...</div></td></tr>
45<tr><td>大连实德</td><td><divid="dlsd">wait...</div></td></tr>
46<tr><td>九寨沟</td><td><divid="jzg">wait...</div></td></tr>
47<tr><td>中国平安</td><td><divid="zgpa">wait...</div></td></tr>
48<tr><td>工商银行</td><td><divid="gsyh">wait...</div></td></tr>
49<tr><td>鞍钢股份</td><td><divid="aggf">wait...</div></td></tr>
50<tr><td>中国航天</td><td><divid="zght">wait...</div></td></tr>
51</table>
52<br>
53</body>
54</html>

4. 报盘的主程序StocksPusher.java ,关键部分在代码后面有中文注释
view source
print?
01package dwr.reverse;
02import java.util.ArrayList;
03import java.util.Collection;
04import java.util.List;
05import org.directwebremoting.WebContext;
06import org.directwebremoting.WebContextFactory;
07import org.directwebremoting.proxy.dwr.Util;
08import org.directwebremoting.util.Logger;
09/**
10* Reverse Ajax class.
11*
12* @author Henry Huang
13*/
14public class StocksPusher {
15private static boolean closeMarket = false;
16/**
17* Initialize the stocklist with values.
18*/
19public StocksPusher() {
20}
21/**
22* Send the Stock-Values to the file "getStockInfo.html"
23*/
24public void sendStocks() throwsInterruptedException {
25WebContext wctx = WebContextFactory.get();//这里是获取WebContext上下文
26String currentPage = wctx.getCurrentPage();//从上下文中获取当前页面,这些是DWR
27Reverse Ajax 要求的必须方式
28Collection sessions = wctx.getScriptSessionsByPage(currentPage);//再一个page中
29可能存在多个ScriptSessions,
30Util utilAll = newUtil(sessions); //Util 是DWR 在Server端模拟Brower端 dwr.util.js
31的类, Engine也是
32while(true){
33Thread.sleep(500);
34if(closeMarket)break;
35StocksBean stock = StockPriceTracer.getNextStockInfo();
36utilAll.setValue(stock.getStock(), stock.getValue());//这里的setValue()用法和
37dwr.util.js中的setValue()函数用法完全一样,第一个参数是页面Element的id ,第二个参数是对该id
38赋的新值
39System.out.println("Pushing stock: "+ stock.getStock() + " = "+
40stock.getValue());
41}
42}
43public void beginShow(){
44closeMarket = false;
45}
46public void closeShow(){
47closeMarket = true;
48}
49}

5. 还有一个类是为了模拟实时获取股票信息的工具StockPriceTracer.java,也可能是访问数据库,
也可能来至卫星的大盘数据,等等,这个类是用随机的方法获得股票价格:
view source
print?
01package dwr.reverse;
02import java.util.ArrayList;
03import java.util.List;
04import java.util.Random;
05import java.util.Stack;
06/**
07* Reverse Ajax class.
08*
09* @author Henry Huang
10*/
11public class StockPriceTracer {
12private static StockPriceTracer tracer = null;
13private List<StocksBean> stocks = new ArrayList<StocksBean>();
14private Stack<StocksBean> cycleStack = new Stack<StocksBean>();
15private StockPriceTracer(){
16stocks.add(newStocksBean("zsy","36.55"));
17stocks.add(newStocksBean("dlsd","91.01"));
18stocks.add(newStocksBean("zsh","22.59"));
19stocks.add(newStocksBean("lggf","5.07"));
20stocks.add(newStocksBean("hedq","71.77"));
21stocks.add(newStocksBean("jdsn","31.61"));
22stocks.add(newStocksBean("yyrj","51.29"));
23stocks.add(newStocksBean("zsyh","52.70"));
24stocks.add(newStocksBean("zgtj","16.96"));
25stocks.add(newStocksBean("sfz","54.34"));
26stocks.add(newStocksBean("jsrj","178.48"));
27stocks.add(newStocksBean("zyd","134.48"));
28stocks.add(newStocksBean("jzg","76.32"));
29stocks.add(newStocksBean("zgpa","80.63"));
30stocks.add(newStocksBean("gsyh","18.79"));
31stocks.add(newStocksBean("aggf","20.19"));
32stocks.add(newStocksBean("zght","11.13"));
33}
34public static StocksBean getNextStockInfo(){
35if(null== tracer) tracer = newStockPriceTracer();
36if(tracer.cycleStack.empty()) tracer.cycleStack.addAll(tracer.stocks);
37StocksBean tmp = tracer.cycleStack.pop();
38tmp.setValue(tracer.getRandomPrice(tmp.getValue()));
39return tmp;
40}
41private String getRandomPrice(String current){
42float fcurrent = 0.0F;
43try{
44fcurrent = Float.parseFloat(current);
45}catch(NumberFormatException e){
46fcurrent = 0.01F;
47}
48Random rdm = newRandom();
49float tmp = fcurrent + rdm.nextFloat();
50return String.valueOf(tmp);
51}
52}

6. 还有一个类是一个JavaBeanStockBean.java
view source
print?
01package dwr.reverse;
02public class StocksBean {
03private String stock = "";
04private String value = "";
05public StocksBean(String stock, String value) {
06this.setStock(stock);
07this.setValue(value);
08}
09public String getStock() {
10return stock;
11}
12public void setStock(String stock) {
13this.stock = stock;
14}
15public String getValue() {
16return value;
17}
18public void setValue(String value) {
19this.value = value;
20}
21}