錢宇虹

摘 要:針對Java Web實時系統中的消息推送這一關鍵問題,本文基于Tomcat8和WebSocket標準,提出并且實現了一個服務端定點推送模型,能夠向指定的Web注冊用戶進行消息推送。該模型具有非廣播的特點,即在沒有瀏覽器請求的情況下通過Servlet主動將消息推送至某個已注冊的Web用戶。該模型已經成功地應用于呼叫中心系統中將電話分機狀態和彈屏信息推送給Web坐席,該模型也可以用于即時消息、游戲應用、實時證券報價、股票系統等Web實時系統,具有廣泛的使用價值。
關鍵詞:Tomcat8;WebSocket;定點推送
中圖分類號:TP311 文獻標識碼:A
1 引言(Introduction)
眾所周知,HTTP協議是基于請求-響應模式的無狀態的單向協議,即每次都要由客戶端(如瀏覽器)主動向服務端發出“請求”,服務端做出處理后將結果作為“響應”返回給客戶端。“無狀態”指的是每次的請求-響應都必須經歷建立連接和釋放連接的過程,前一次連接和后一次連接相互之間沒有關系。“單向”指的是客戶端是主動方,服務端是被動方,服務端不能主動向客戶端發送數據。HTTP協議的這一特點對傳統的Web應用,像電子商務網站、搜索引擎等等非常合適,但是不能滿足日益增強的實時性需求的Web應用,這些應用要求在無需客戶端發出請求的前提下,服務端能實時地將信息主動推送到客戶端,如基于Web的聊天系統、即時消息、游戲應用、實時證券報價和股票系統等。
過去,針對實時性較強的應用,開發人員使用輪詢和Comet技術這些折中方案。輪詢就是客戶端按照預先設置的時間間隔向服務端發送請求,周期性地查詢是否有數據更新。早期的輪詢是通過在HTML頭部插入META元信息來實現網頁的自動刷新,后來人們使用AJAX輪詢。AJAX輪詢帶來的最大好處就是在不刷新整個頁面的前提下可以實現網頁的局部更新,既提高了做事的效率又增強了用戶體驗,但是AJAX還是受限于請求-響應模式,由于無法預期什么時候推送消息,造成很多無效的請求。而Comet是使用一種新的技術去做輪詢得到的效果,這種技術雖然可以實現雙向通信,但是依然需要發出請求,而且Comet普遍采用長連接,這會導致大量消耗服務器帶寬和資源。Comet技術本質上還是沒有擺脫HTTP請求-響應模式,不能算是真正意義上的雙向通信,并且開發復雜度也較高。
隨著HTML5推出WebSocket,為真正解決服務器推送提供了技術保障。該規范旨在瀏覽器中實現和服務器端的雙向通信,用以拓展瀏覽器上的應用類型,如實時監控系統[1]、校園通知系統[2]。WebSocket規范實際上由兩部分組成,一部分是瀏覽器中的WebSocket API,由W3C制訂;一部分是WebSocket協議,由IETF制訂。它引入WebSocket接口,在WEB上通過一個單一的套接字(Socket)定義了一個全雙工的通信通道,與通過維持兩個連接來模擬全雙工通信的輪詢和長輪詢方案相比,WebSocket大幅降低了不必要的網絡流量和延遲[3]。WebSocket是為解決WEB客戶端與服務端實時通信而產生的技術,協議設計的一個重要原則就是能和現有的Web方式和睦共處,其本質是首先通過HTTP/HTTPS協議進行握手后創建一個用于交換數據的TCP連接,此后WEB客戶端與服務端就此TCP連接進行實時通信[4]。
2 Tomcat8對WebSocket的支持(Tomcat8 support
for WebSocket)
WebSocket在瀏覽器端的實現遵循標準的HTML5,這個和服務器采用Tomcat或者Jetty是無關的,標準化的形式是WebSocket JavaScript API[5],主流的瀏覽器上都得到很好的支持,所以通過JavaScript書寫Web客戶端的代碼既標準也較為簡單。值得大家注意的是服務器端的實現在形成統一標準之前,各個實現都有自己的一套API,所以使用WebSocket開發服務器端存在一定的風險。
Tomcat7.0.27是Tomcat支持WebSocket的第一個版本,其關鍵是提供了org.apache.catalina.websocket.WebSocketServlet接口,在Tomcat7.0.27中需要一個Servlet來處理WebSocket請求,這個Servlet要繼承自WebSocketServlet這個類,然后實現createWebSocketInbound方法,該方法返回StreamInbound對象。這個接口是非標準的WebSocket實現,到Tomcat8中,WebSocketServlet和StreamInbound這兩個類都過期了,所以基于Tomcat7.0.27來實現服務器推送的技術在這里不再贅述[6]。
在Java社區技術的進步常常經歷這樣的階段,就是不同的供應商和開發人員編寫類庫實現某種技術,隨著時間的推移當該技術成熟時它就被標準化,使得開發者能夠在不同的實現之間相互操作,避免冒鎖定特定供應商的風險。JSR356就是把WebSocket的Java API進行標準化的結果,它已經成為JavaEE 7標準的一部分,這意味著所有JavaEE 7兼容的應用服務器都遵守JSR356的WebSocket協議標準。
Tomcat8是真正支持JSR356標準的,它自帶的WebSocket API包是在Tomcat8安裝目錄下的lib目錄中的websocket-api.jar[7],我們需要此jar包設置到項目的classpath中。在Tomcat8中使用WebSocket的代碼如下:
(1)Web客戶端(JavaScript代碼,與服務器類型無關)