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