长连接/websocket/SSE等主流服务器推送技术比较
最近做的某個項目有個需求,需要實時提醒client端有線上訂單消息。所以保持客戶端和服務器端的信息同步是關鍵要素,對此我們了解了可實現的方式。本文將介紹web常用的幾種方式,希望給需要服務器端推送消息的同學在選型上有一點啟發。
一、推送技術常用的集中實現的實現方式
1.1 短連接輪詢:
前端用定時器,每間隔一段時間發送請求來獲取數據是否更新,這種方式可兼容ie和支持高級瀏覽器。通常采取setInterval或者setTimeout實現。
(輪詢示意圖)通過遞歸的方法,在獲取到數據后每隔一定時間再次發送請求,這樣雖然無法保證兩次請求間隔為指定時間,但是獲取的數據順序得到保證。
- 缺點:
1、頁面會出現‘假死’
setTimeout在等到每次EventLoop時,都要判斷是否到指定時間,直到時間到再執行函數,一旦遇到頁面有大量任務或者返回時間特別耗時,頁面就會出現‘假死’,無法響應用戶行為。
2、無謂的網絡傳輸
當客戶端按固定頻率向服務器發起請求,數據可能并沒有更新,浪費服務器資源。
1.2 長輪詢:
客戶端像傳統輪詢一樣從服務端請求數據,服務端會阻塞請求不會立刻返回,直到有數據或超時才返回給客戶端,然后關閉連接,客戶端處理完響應信息后再向服務器發送新的請求。
(輪詢示意圖)長輪詢解決了頻繁的網絡請求浪費服務器資源可以及時返回給瀏覽器。
- 缺點:
1、保持連接會消耗資源。
2、服務器沒有返回有效數據,程序超時。
1.3 iframe流:
iframe流方式是在頁面中插入一個隱藏的iframe,利用其src屬性在服務器和客戶端之間創建一條長連接,服務器向iframe傳輸數據(通常是HTML,內有負責插入信息的javascript),來實時更新頁面。
- 前端實現步驟:
1、Iframe設置為不顯示。
2、src設為請求的數據地址。
3、定義個父級函數用戶讓iframe子頁面調用傳數據給父頁面。
4、定義onload事件,服務器timeout后再次重新加載iframe。
- 后端輸出內容:
當有新消息時服務端會向iframe中輸入一段js代碼.:println("<script>父級函數('" + 數據 +"<br>')</script>”);用于調用父級函數傳數據。
- 優點:
iframe流方式的優點是瀏覽器兼容好,Google公司在一些產品中使用了iframe流,如Google Talk。
- 缺點:
1、IE、Mozilla Firefox會顯示加載沒有完成,圖標會不停旋轉。
2、服務器維護一個長連接會增加開銷。
1.4 WebSocket:
WebSocket是一種全新的協議,隨著HTML5草案的不斷完善,越來越多的現代瀏覽器開始全面支持WebSocket技術了,它將TCP的Socket(套接字)應用在了webpage上,從而使通信雙方建立起一個保持在活動狀態連接通道。
- 原理:
WebSocket協議是借用HTTP協議的101 switchprotocol(服務器根據客戶端的指定,將協議轉換成為 Upgrade首部所列的協議)來達到協議轉換的,從HTTP協議切換成WebSocket通信協議。
- 具體連接方式:
通過在請求頭中增加 upgrade:websocket 及通信密鑰(Sec-WebSocket-Key),使雙方握手成功,建立全雙工通信。
(WebSocket客戶端連接報文) (WebSocket服務端響應報文)- 通信過程:
websocket是純事件驅動的,一旦 WebSocket 連接建立后,通過監聽事件可以處理到來的數據和改變的連接狀態。數據都以幀序列的形式傳輸。服務端發送數據后,消息和事件會異步到達。WebSocket編程遵循一個異步編程模型,只需要對WebSocket對象增加回調函數就可以監聽事件。
(websocket示意圖)前端:
服務端:
1.5 Server-sent Events(sse):
sse與長輪詢機制類似,區別是每個連接不只發送一個消息。客戶端發送一個請求,服務端保持這個連接直到有新消息發送回客戶端,仍然保持著連接,這樣連接就可以消息的再次發送,由服務器單向發送給客戶端。
- 原理:
SSE本質是發送的不是一次性的數據包,而是一個數據流。可以使用 HTTP 301 和 307 重定向與正常的 HTTP 請求一樣。服務端連續不斷的發送,客戶端不會關閉連接,如果連接斷開,瀏覽器會嘗試重新連接。如果連接被關閉,客戶端可以被告知使用 HTTP 204 無內容響應代碼停止重新連接。
sse只適用于高級瀏覽器,ie不支持。因為ie上的XMLHttpRequest對象不支持獲取部分的響應內容,只有在響應完成之后才能獲取其內容。
二、常用實現的對比
三、項目選型
Websocket需要服務器重新部署,sse可以利用原先的http協議,而我們項目是在高級瀏覽器環境,場景是需要服務器單向發送給客戶端,所以sse更符合我們的需求。
四、項目實踐
A應用下單完成后,把訂單消息放入到redis緩存中,B應用去獲取redis緩存信息判斷是否是新訂單,否的情況輪詢redis緩存,是的情況消息推送給前端。
(后端流程圖)客戶端:
然后使用onmessage事件來獲取消息
服務端可以自定義類型的事件,對于這些事件,可以使用addEventListener來獲取。
服務端:
內容與普通的Controller差不多。只不過相應的方法在路由配置時,將produces屬性的文本類型設置成“text/event-stream”即可。
首先通過設置唯一標識符+venueid,獲取相應場館保存在redis中的訂單。
常見問題及解決方案:
1、怎么確定推過來的消息是新消息
這里我們設置了一個本地緩存,用來存放上一次從redis中獲取的信息,和當前從redis獲取的信息做對比,不同,則認為是新信息返回給客戶端并標識是新數據。
2、刷新頁面原先推送過來的消息消失了
因為在通過redis和本地緩存對比的時候沒有區別所以不會推送,這里前端設置一個隨機數num,在存入本地緩存時key值多加了num的區分。
3、解決容器超時的問題
后端容器的單個連接超時時間為2分鐘,后端每隔3秒鐘會輪詢一次redis,到第20次的時候,會推送個帶有個標識的數據。
4、接口防刷方案
后端記錄每次獲取到的num值判斷總數vnum,超過一定數量返回http 204斷開連接。
總結
對于簡單的推送需求又不考慮兼容低版本瀏覽器,推薦使用server-sent Events。
如果需要多條雙向數據實時交互或需要二進制傳輸,推薦websocket。
對于還要考慮低版本瀏覽器,那么還是用輪詢來實現功能。
【作者介紹】本文由攜程市場營銷研發部武藝嬙和王宇星以及張子祥共同撰寫,武藝嬙在市場營銷研發部負責前端,王宇星和張子祥在市場營銷研發部負責java后端。
https://zhuanlan.zhihu.com/p/31297574
總結
以上是生活随笔為你收集整理的长连接/websocket/SSE等主流服务器推送技术比较的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 京东JIMI用户未来意图预测技术揭秘
- 下一篇: 携程基于大数据分析的实时风控体系