傳統模式的 Web 系統以客戶端發出請求、服務器端響應的方式工作。不能滿足很多現實應用的需求,譬如:
監控系統:後臺硬件溫度、電壓發生變化;
即時通信系統:其它用戶登錄、發送信息;
即時報價系統:後臺數據庫內容發生變化;
即時信息系統:微博、說說實時推送
目前主流的是採取如下幾種方式來實現以上需求:
Ajax輪詢:異步響應機制,即通過不間斷的客戶端 Ajax 請求,去發現服務端的變化。這種方式由於是客戶端主動連接的,所以會有一定程度的延時,並且服務器的壓力也不小。
長輪詢:原理是客戶端發出一個http長連接請求,然後等待服務器的響應,服務器接到請求之後,並不立即發送出數據,而是hold住這個Connecton。這個處理是非阻塞的,所以服務器可以繼續處理其他請求。在某個時刻,比如服務器有新數據了,服務器再主動把這個消息推送出去,即通過之前建立好的連接將數據推送給客戶端。客戶端收到返回。這個時候就可以處理數據,然後再次發起新的長連接。服務器壓力一般,實時性很高。Servlet 3.0開始已經支持該技術。sina微博就是使用的原生Servlet 3實現的消息推送。
套接字:可以利用 Flash 的 XMLSocket 類或者 Java 的 Applet 來建立 Socket 連接,實現全雙工的服務器推送,然後通過 Flash 或者Applet 與 JavaScript 通信的接口來實現最終的數據推送。但是這種方式需要 Flash 或者 JVM 的支持,同樣不太合適於終端用戶。
HTML5的WebSocket:這種方式其實與套接字一樣,但是這裏需要單獨強調一下:它是不需要用戶而外安裝任何插件的。HTML5 提供了一個 WebSocket 的 JavaScript 接口,可以直接與服務端建立Socket 連接,實現全雙工通信,這種方式的服務器推送就是完全意義上的服務器推送了,沒有半點模擬的成分,只是現階段支持 HTML5 的瀏覽器並不多,而且一般老版本的各種瀏覽器基本都不支持。不過 HTML5 是一套非常好的標準,在將來,當HTML5 流行起來以後將是我們實現服務器推送技術的不二選擇。
Ajax輪訓壓力大,套接字不適用,HTML5目前支持不大多,這樣看來長輪詢是我們的必然之選。 可是使用 servlet 3 實現自己要做的事很多。沒有有什麼可供選擇的框架呢?當然是有了,如下:
1、Cometdhttp://cometd.org/ 基於Bayeux協議實現,支持長輪詢、WebSocket等.. 2、Pushlethttp://www.pushlets.com/ 基於HTTP流的JSP/Semlet技術實現 3、Atmospherehttp://atmosphere.java.net/ 4、DWRhttp://directwebremoting.org/dwr/index.html
據我的瞭解,使用最多的應該是 DWR 和 Cometd了,其中Cometd功能強大使用又簡單。我參與的一個項目中使用Cometd 做前臺地圖上人員軌跡的實時推送,幾百個人員的實時軌跡推送都是沒問題的。用。Cometd前臺有2個實現版本 Jquery 和 Dojo,也可以用JS但是會複雜點。(PS Cometd 官方文檔相當使用,遇到問題就讀一遍,肯定能找到你想要的。 http://docs.cometd.org/reference/)
如果你安裝Maven了,以下2個簡單的命令就能讓你體驗一把推送效果
下面下載包我在項目中使用的實例。實例下載,請猛擊這裏 傳送門
你可能想向前臺傳送的數據是一個類而不僅僅是字符串,那麼你可以使用Jackson讓cometd幫你做自動的轉換,而你的Java代碼中是直接向客戶端輸出對象。甚至是List都可以。你需要如下操作:1、向前端輸出的對象必須實現 Serializable , Cloneable 和 JSON.Convertible 接口
import java.util.Date;import java.util.Map;import org.eclipse.jetty.util.ajax.JSON;import org.eclipse.jetty.util.ajax.JSON.Output;import com.yixun.util.TimeUtil;public class Alarmrecord implements java.io.Serializable , Cloneable, JSON.Convertible{ // Fields private Long recordid; private Readerinfo readerinfo; private Cardinfo cardinfo; private Position position; private Alarminfo alarminfo; private String alarmdetail; private Date recordtime; private String alarmType; private String alarmState; /** * @see org.eclipse.jetty.util.ajax.JSON$Convertible#fromJSON(java.util.Map) * @author simon * create on Apr 14, 2012 11:58:41 AM */ public void fromJSON(Map map) { this.recordid = (Long) map.get("recordid"); this.readerinfo = (Readerinfo) map.get("readerinfo"); this.cardinfo = (Cardinfo) map.get("cardinfo"); this.position = (Position) map.get("position"); this.alarminfo = (Alarminfo) map.get("alarminfo"); this.alarmdetail = (String) map.get("alarmdetail"); this.alarmType = (String) map.get("alarmType"); this.alarmState = (String) map.get("alarmState"); this.recordtime = (Date) map.get("recordtime"); } /** * @see org.eclipse.jetty.util.ajax.JSON$Convertible#toJSON(org.eclipse.jetty.util.ajax.JSON.Output) * @author simon * create on Apr 14, 2012 11:58:46 AM */ public void toJSON(Output out) { out.add("recordid", recordid); out.add("readerinfo", readerinfo); out.add("cardinfo", cardinfo); out.add("position", position); out.add("alarminfo", alarminfo); out.add("alarmdetail", alarmdetail); out.add("recordtime", TimeUtil.format(recordtime,"yyyy-MM-dd HH:mm:ss")); out.add("alarmType", alarmType); out.add("alarmState", alarmState); }}
2、Web.xml 中,org.cometd.server.CometdServlet 下添加 使用Jetty json 的參數。如下圖:
3、OK,這樣你後臺就可以直接這樣寫來向前臺寫數據了,cometd會幫你做對象到Json的轉換。
4、前臺這樣獲取數據:
5、另外有一點要說的是 如果你向前臺寫的對象 AlarmRecord中包含其他對象如Positon ,那麼Position對象也要實現 Serializable , Cloneable 和 JSON.Convertible 接口