dwr推送技術深入研究
原文地址:https://www.cnblogs.com/momo1210/p/6283578.html
DWR 工作原理:
是通過動態把 Java 類生成為 Javascript。它的程式碼就像 Ajax 一樣,你感覺呼叫就像發生在瀏覽器端,但是實際上程式碼呼叫發生在伺服器端,DWR 負責資料的傳遞和轉換。這種從 Java 到JavaScript的遠端呼叫功能的方式使DWR用起來有種非常像RMI或者SOAP的常規RPC機制,而且DWR的優點在於不需要任何的網頁瀏覽器外掛就能執行在網頁上。
Java 從根本上講是同步機制,然而 AJAX 卻是非同步的。所以你呼叫遠端方法時,當資料已經從網路上返回的時候,你需要提供有反調 (callback) 功能的 DWR,即你的js程式碼呼叫最後一個引數是一個回撥函式,由回撥函式處理資料返回後想做的操作。
Comet
Comet方式通俗的說就是一種長連線機制(long lived http)。同樣是由Browser端主動發起請求,但是Server端以一種似乎非常慢的響應方式給出回答。這樣在這個期間內,伺服器端可以使用同一個connection把要更新的資料主動傳送給Browser。因此請求可能等待較長的時間,期間沒有任何資料返回,但是一旦有了新的資料,它將立即被髮送到客戶機。Comet又有很多種實現方式,但是總的來說對Server端的負載都會有增加.雖然對於單位操作來說,每次只需要建議一次connection,但是由於connection是保持較長時間的,對於 server端的資源的佔用要有所增加。
優點: 實時性好(訊息延時小);效能好(能支援大量使用者)
缺點: 長期佔用連線,喪失了無狀態高併發的特點。
應用: 股票系統、實時通訊。
1.先配置web.xml
<!-- dwr start --> <servlet> <!-- dwr框架的核心控制器 --> <servlet-name>dwr-invoker</servlet-name> <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class> <!-- 在伺服器端啟用反向ajax --> <init-param> <param-name>activeReverseAjaxEnabled</param-name> <param-value>true</param-value> </init-param> <!-- 為了得到更詳細的除錯資訊 --> <init-param> <param-name>debug</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>crossDomainSessionSecurity</param-name> <param-value>false</param-value> </init-param> <init-param> <param-name>allowScriptTagRemoting</param-name> <param-value>true</param-value> </init-param> <init-param> <param-name>org.directwebremoting.extend.ScriptSessionManager </param-name> <param-value>com.worklite.core.tools.dwr.DWRScriptSessionManager</param-value> </init-param> <!-- 在伺服器啟動階段,該servlet例項被建立 --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>dwr-invoker</servlet-name> <!-- 傳送如下的請求,都會交給dwr的核心控制器處理 --> <url-pattern>/dwr/*</url-pattern> </servlet-mapping> <!-- dwr end -->
2.新建dwr.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 3.0//EN" "http://getahead.org/dwr/dwr30.dtd"> <dwr> <allow> <create creator="new" javascript="DwrPush"> <param name="class" value="com.worklite.core.tools.dwr.DwrPush"/> </create> <create creator="new" javascript="ClientInitUtil"> <param name="class" value="com.worklite.core.tools.dwr.ClientInitUtil"/> </create> <create creator="new" javascript="WebMultiplePushUtil"> <param name="class" value="com.worklite.core.tools.dwr.WebMultiplePushUtil"/> </create> <create creator="new" javascript="WebSinglePushUtil"> <param name="class" value="com.worklite.core.tools.dwr.WebSinglePushUtil"/> </create> <create creator="new" javascript="WebSingleGamePushUtil"> <param name="class" value="com.worklite.core.tools.dwr.WebSingleGamePushUtil"/> </create> </allow> </dwr>
3.編寫後臺需要的類。
(1)DWRScriptSessionListener
public class DWRScriptSessionListener implements ScriptSessionListener { //維護一個Map key為session的Id, value為ScriptSession物件 public static final Map<String, ScriptSession> scriptSessionMap = new MirrorMap<String, ScriptSession>(); /** * ScriptSession建立事件 */ @Override public void sessionCreated(ScriptSessionEvent event) { WebContext webContext = WebContextFactory. get(); HttpSession session = webContext.getSession(); ScriptSession scriptSession = event.getSession(); scriptSessionMap.put(session.getId(), scriptSession); //新增scriptSession } /** * ScriptSession銷燬事件 */ @Override public void sessionDestroyed(ScriptSessionEvent event) { //WebContext webContext = WebContextFactory. get(); //HttpSession session = webContext.getSession(); //scriptSessionMap.remove(session.getId()); //移除scriptSession } /** * 獲取所有ScriptSession */ public static Collection<ScriptSession> getScriptSessions(){ return scriptSessionMap.values(); } public static ScriptSession getScriptSession(String str){ Collection<ScriptSession> sessions = DWRScriptSessionListener.getScriptSessions(); //遍歷每一個ScriptSession for (ScriptSession scriptSession : sessions){ String tag = (String)scriptSession.getAttribute(str); if ( tag!=null&&str.equals(tag)) { return scriptSession; } } return null; } public static int Count(String wId){ Collection<ScriptSession> sessions = DWRScriptSessionListener.getScriptSessions(); //遍歷每一個ScriptSession int i=0; for (ScriptSession scriptSession : sessions){ String tag = (String)scriptSession.getAttribute(wId); if ( tag!=null&&wId.equals(tag)) { i++; } } return i; } public static boolean getCustomList(String wId,int customId){ Collection<ScriptSession> sessions = DWRScriptSessionListener.getScriptSessions(); //遍歷每一個ScriptSession for (ScriptSession scriptSession : sessions){ String tagId=customId+wId; String tag = (String)scriptSession.getAttribute(tagId); if(tag!=null&&tagId.equals(tag)){ return true; } } return false; } public static List<String> getCustomList(String wId){ List<String> customList=new ArrayList<String>(); Collection<ScriptSession> sessions = DWRScriptSessionListener.getScriptSessions(); //遍歷每一個ScriptSession for (ScriptSession scriptSession : sessions){ Iterator<String> iter = scriptSession.getAttributeNames(); while(iter.hasNext()){ String str=iter.next(); if(str!=null&&str.contains(String.valueOf(","+wId))){ String customId = str.split(",")[0]; if (!customList.contains(customId)) customList.add(customId); } } } return customList; } public void init() throws ServletException { Container container = ServerContextFactory.get().getContainer(); ScriptSessionManager manager = container.getBean(ScriptSessionManager.class); ScriptSessionListener listener = new ScriptSessionListener() { public void sessionCreated(ScriptSessionEvent ev) { WebContext webContext = WebContextFactory. get(); HttpSession session = webContext.getSession(); ScriptSession scriptSession = ev.getSession(); scriptSessionMap.put(session.getId(), scriptSession); //新增scriptSession } public void sessionDestroyed(ScriptSessionEvent ev) { WebContext webContext = WebContextFactory. get(); HttpSession session = webContext.getSession(); ScriptSession scriptSession = scriptSessionMap.remove(session.getId()); //移除scriptSession } }; manager.addScriptSessionListener(listener); } }
(2)DWRScriptSessionManager
public class DWRScriptSessionManager extends DefaultScriptSessionManager { public DWRScriptSessionManager(){ //繫結一個ScriptSession增加銷燬事件的監聽器 this.addScriptSessionListener( new DWRScriptSessionListener()); Log.debug( "bind DWRScriptSessionListener"); } }
(3)DwrPush註冊scriptSession
public class DwrPush { public void onPageLoad(final String tag){ //獲取當前的ScriptSession ScriptSession scriptSession = WebContextFactory.get().getScriptSession(); scriptSession.setAttribute( tag, tag); } }
(4)WebMultiplePushUtil 用於群發
public class WebMultiplePushUtil { public void push(final String wId,final String content,final String command){ //過濾器 ScriptSessionFilter filter = new ScriptSessionFilter() { public boolean match(ScriptSession scriptSession) { String tag = (String)scriptSession.getAttribute(wId); if ( tag== null) { return false; } else { return wId.equals(tag); } } }; Runnable run = new Runnable(){ private ScriptBuffer script = new ScriptBuffer(); public void run() { //設定要呼叫的 js及引數 script.appendCall( "showMessage","{\"wId\":\""+wId+"\",\"content\":\""+content+"\",\"command\":\""+command+"\"}"); //得到所有ScriptSession Collection<ScriptSession> sessions = Browser.getTargetSessions(); //遍歷每一個ScriptSession for (ScriptSession scriptSession : sessions){ scriptSession.addScript( script); } } }; //執行推送 Browser. withAllSessionsFiltered(filter, run); //注意這裡呼叫了有filter功能的方法 } }
(5)WebSingleGamePushUtil 用於單發遊戲
public class WebSingleGamePushUtil { public void push(final String customId,final String wId,final Webcast_game_push push){ Runnable run = new Runnable(){ private ScriptBuffer script = new ScriptBuffer(); public void run() { //設定要呼叫的 js及引數 script.appendCall( "show",JSONObject.fromObject(push).toString()); ScriptSession scriptSession =DWRScriptSessionListener.getScriptSession(customId+","+wId); if(null!=scriptSession){ scriptSession.addScript( script); } } }; //執行推送 Browser.withAllSessions(run); } }
(6)WebSinglePushUtil 用於單發通知
public class WebSinglePushUtil { public void push(final String customId,final String wId,final String content,final String command){ //過濾器 ScriptSessionFilter filter = new ScriptSessionFilter() { public boolean match(ScriptSession scriptSession) { String tagId=customId+","+wId; String tag = (String)scriptSession.getAttribute(tagId); if ( tag== null) { return false; } else { return tagId.equals(tag); } } }; Runnable run = new Runnable(){ private ScriptBuffer script = new ScriptBuffer(); public void run() { //設定要呼叫的 js及引數 script.appendCall( "show","{\"customId\":\""+customId+"\",\"wId\":\""+wId+"\",\"content\":\""+content+"\",\"command\":\""+command+"\"}"); //得到所有ScriptSession Collection<ScriptSession> sessions = Browser.getTargetSessions(); //遍歷每一個ScriptSession for (ScriptSession scriptSession : sessions){ scriptSession.addScript( script); } } }; //執行推送 Browser. withAllSessionsFiltered(filter, run); //注意這裡呼叫了有filter功能的方法 } }
4.在jsp頁面引入相應的js
<script type="text/javascript" src="/worklite/dwr/engine.js"></script> <script type="text/javascript" src="/worklite/dwr/util.js"></script> <script type="text/javascript" src="/worklite/dwr/interface/DwrPush.js"></script> dwr.engine.setActiveReverseAjax(true); dwr.engine.setAsync(true); dwr.engine._errorHandler = function(message, ex) {dwr.engine._debug("Error: " + ex.name + ", " + ex.message, true);}; <body onload="dwr.engine.setActiveReverseAjax(true);dwr.engine.setNotifyServerOnPageUnload(true);onPageLoad();">
在頁面上要先掉用下DwrPush.onPageLoad(),加入scriptSession
function onPageLoad(){ DwrPush.onPageLoad(wId); DwrPush.onPageLoad(""+customId+","+wId); }
然後在程式碼中呼叫相應的push方法即可完成推送。