本文做者網易智慧企業web前端開發工程師馬瑩瑩。爲了提高內容質量,收錄時有修訂和改動。php
在一個完善的即時通信IM應用中,WebSocket是極其關鍵的一環,它爲基於Web的即時通信應用提供了一種全雙工的通訊機制。但爲了提高IM等實際應用場景下的消息即時性和可靠性,咱們須要克服WebSocket及其底層依賴的TCP鏈接對於複雜網絡狀況下的不穩定性,即時通信的開發者們一般都須要爲其設計一套完整的鏈接保活、驗活以及斷片網重連方案。html
就斷網重連而言,其重連響應速度將嚴重影響了上層應用的「即時性」和用戶體驗。試想打開網絡一分鐘後,微信的網絡不能即時感知到socket鏈接的恢復,沒法即時收發聊天消息的話,是否是很崩潰?前端
所以,如何在複雜網絡場景下,更即時快速地感知網絡變更,並快速恢復WebSocket的可用性,就變得尤其重要。本文將基於筆者的開發實踐,分享WebSocket在不一樣狀態下、不一樣的網絡狀態下,應該如何實現快速斷網重連。web
* 閱讀對象:本文適合有過IM底層網絡實際開發經驗,或者對底層網絡實現有較深瞭解的開發者閱讀。若是對底層網絡瞭解甚少,建議跳過本文,直接閱讀網絡本文末尾附錄部分的基礎後再回頭來看。算法
* 內容點評:本文內容沒有高大上,但比較乾貨,實用性較高,內容也很通俗,建議可詳細閱讀。文中雖講的是WebSocket,但思想能夠延伸應用到基於TCP協議的同類技術中。小程序
本文已同步發佈於「即時通信技術圈」公衆號,歡迎關注:微信小程序
▲ 本文在公衆號上的連接是:點此進入,原文連接是:http://www.52im.net/thread-3098-1-1.html跨域
本文中將要分享的內容是基於實踐總結,若是你對Web端的即時通信知識還一頭霧水,務必先讀:《新手入門貼:史上最全Web端即時通信技術原理詳解》、《Web端即時通信技術盤點:短輪詢、Comet、Websocket、SSE》。瀏覽器
限於篇幅,本文不會深究WebSocket技術細節,若有興趣請系統學習:安全
Websocket誕生於2008年,在2011年成爲國際標準,如今全部的瀏覽器都已支持(詳見《新手快速入門:WebSocket簡明教程》)。它是一種全新的應用層協議,是專門爲web客戶端和服務端設計的真正的全雙工通訊協議,能夠類比HTTP協議來了解websocket協議。
(圖片引用自《WebSocket詳解(四):刨根問底HTTP與WebSocket的關係(上篇)》)
它們的不一樣點:
它們的相同點:
二者和TCP的關係圖:
(圖片引用自《新手快速入門:WebSocket簡明教程》)
有關Http和WebSocket的關係,能夠詳讀:
有關WebSocket和Socket的關係,能夠詳讀:《WebSocket詳解(六):刨根問底WebSocket與Socket的關係》.
首先考慮一個問題,什麼時候須要重連?
最容易想到的是WebSocket鏈接斷了,爲了接下來能收發消息,咱們須要再發起一次鏈接。
但在不少場景下,即使WebSocket鏈接沒有斷開,實際上也不可用了。
好比如下場景:
這些場景下的WebSocket都沒有斷開,但對上層來講,都沒辦法正常的收發數據了。
所以在重連前,咱們須要一種機制來感知鏈接是否可用、服務是否可用,並且要能快速感知,以便可以快速從不可用狀態中恢復。
一旦感知到了鏈接不可用,那即可以棄舊圖新了,棄用並斷開舊鏈接,而後發起一次新鏈接。這兩個步驟看似簡單,但若想達到快,且不是那麼容易的。
首先:是斷開舊鏈接,對客戶端來講,如何快速斷開?協議規定客戶端必需要和服務器協商後才能斷開WebSocket鏈接,可是當客戶端已經聯繫不上服務器、沒法協商時,如何斷開並快速恢復?
其次:是快速發起新鏈接。此快非彼快,這裏的快並不是是當即發起鏈接,當即發起鏈接會對服務器帶來不可預估的影響。重連時一般會採用一些退避算法,延遲一段時間後再發起重連。但如何在重連間隔和性能消耗間作出權衡?如何在「恰當的時間點」快速發起鏈接?
帶着這些疑問,咱們來細看下這三個過程:
須要重連的場景能夠細分爲三種:
對於第一種場景:這很簡單,鏈接直接斷開了,確定須要重連了。
對於後二者:不管是鏈接不可用,仍是服務不可用,對上層應用的影響都是不能再收發即時消息了。
因此從上面這個角度出發,感知什麼時候須要重連的一種簡單粗暴的方法就是經過心跳包超時:發送一個心跳包,若是超過特定的時間後尚未收到服務器回包,則認爲服務不可用,以下圖中左側的方案(這種方法最直接)。
那若是想要快速感知呢,就只能多發心跳包,加快心跳頻率。可是心跳太快對移動端流量、電量的消耗又會太多,因此使用這種方法沒辦法作到快速感知,能夠做爲檢測鏈接和服務可用的兜底機制。
若是要檢測鏈接不可用,除了用心跳檢測,還能夠經過判斷網絡狀態來實現,由於斷網、切換wifi、切換網絡是致使鏈接不可用的最直接緣由,因此在網絡狀態由offline變爲online時,大多數狀況下須要重連下,但也不必定,由於webscoket底層是基於TCP的,TCP鏈接不能敏銳的感知到應用層的網絡變化,因此有時候即使網絡斷開了一小會,對WebSocket鏈接是不會有影響的,網絡恢復後,仍然可以正常地進行通訊。
所以在網絡由斷開到鏈接上時,當即判斷下鏈接是否可用,能夠經過發一個心跳包判斷,若是可以正常收到服務器的心跳回包,則說明鏈接還是可用的,若是等待超時後仍沒有收到心跳回包,則須要重連,如上圖中的右側。這種方法的優勢是速度快,在網絡恢復後可以第一時間感知鏈接是否可用,不可用的話能夠快速執行恢復,但它只能覆蓋應用層網絡變化致使WebSocket不可用的狀況。
綜上所述:
所以,咱們能夠結合兩種方案:
這樣在大多數狀況下,上層的應用通訊都能較快從不可用狀態中恢復,對於少部分場景,有定時心跳做爲兜底,在一個心跳週期內也可以恢復。
一般狀況下,在發起下一次鏈接前,若是舊鏈接還存在的話,應該先把舊鏈接斷開。
這樣作的目的:
咱們知道WebSocket底層是基於TCP協議傳輸數據的,鏈接兩端分別是服務器和客戶端,而TCP的TIME_WAIT狀態是由服務器端維持的,所以在大多數正常狀況下,應該由服務器發起斷開底層TCP鏈接,而不是客戶端。
也就是說:
那若是客戶端想要斷開舊的WebSocket,能夠分爲WebSocket鏈接可用和不可用兩種狀況來討論。
具體以下:
超時斷開的過程相對來講是比較久的,那有沒有辦法能夠快點斷開?
上層應用沒法改變只能由服務器發起斷開鏈接這種協議層面的規則,因此只能從應用邏輯入手,好比在上層經過業務邏輯保證舊鏈接徹底失效,模擬鏈接斷開,而後在發起新鏈接,恢復通信。
這種方法至關於嘗試斷開舊鏈接不行時,直接棄之,而後就能快速進入下一流程,因此在使用時必定要確保在業務邏輯上舊鏈接已徹底失效。
好比:
有IM開發經驗的同窗應該有所瞭解,遇到因網絡緣由致使的重連時,是萬萬不能當即發起一次新鏈接的,不然當出現網絡抖動時,全部的設備都會當即同時向服務器發起鏈接,這無異於黑客經過發起大量請求消耗網絡帶寬引發的拒絕服務攻擊,這對服務器來講簡直是災難(即:服務端雪崩效應)。
因此在重連時一般採用一些退避算法,延遲一段時間再發起重連,以下圖中左側的流程。
若是要快速連上呢?最直接的作法就是縮短重試間隔,重試間隔越短,在網絡恢復後就能越快的恢復通信。可是太頻繁的重試對性能、帶寬、電量的消耗就比較嚴重。
如何在這之間作一個較好的權衡呢?
上述第2)種方案,如上圖中的右側所示,隨重試次數的增多,重連間隔也會變大。這兩種方式配合使用,更爲合理。
除此以外,還能夠結合業務邏輯,根據成功重連上的可能性適當的調整間隔,如網絡未鏈接時或應用在後臺時重連間隔能夠調大一些,網絡正常的狀態下能夠適當調小一些等等,加快重連上的速度。
最後總結一下。
本文將WebSocket斷網重連邏輯細分爲三個步驟:
而後分別分析了在WebSocket的不一樣狀態下、不一樣的網絡狀態下,如何快速完成這個三個步驟。
過程具體總結就是:
以上就是我關於如何實現WebSocket快速重連的技術分享,歡迎留言與我探討。
[1] RFC 6455 文檔
[3] WebSocket詳解(四):刨根問底HTTP與WebSocket的關係(上篇)
[4] WebSocket詳解(五):刨根問底HTTP與WebSocket的關係(下篇)
[5] WebSocket詳解(六):刨根問底WebSocket與Socket的關係
《 新手入門貼:史上最全Web端即時通信技術原理詳解》
《 Web端即時通信技術盤點:短輪詢、Comet、Websocket、SSE》
《 SSE技術詳解:一種全新的HTML5服務器推送事件技術》
《 Comet技術詳解:基於HTTP長鏈接的Web端實時通訊技術》
《 socket.io實現消息推送的一點實踐及思路》
《 LinkedIn的Web端即時通信實踐:實現單機幾十萬條長鏈接》
《 Web端即時通信技術的發展與WebSocket、Socket.io的技術實踐》
《 Web端即時通信安全:跨站點WebSocket劫持漏洞詳解(含示例代碼)》
《 開源框架Pomelo實踐:搭建Web端高性能分佈式IM聊天服務器》
《 使用WebSocket和SSE技術實現Web端消息推送》
《 詳解Web端通訊方式的演進:從Ajax、JSONP 到 SSE、Websocket》
《 MobileIMSDK-Web的網絡層框架爲什麼使用的是Socket.io而不是Netty?》
《 理論聯繫實際:從零理解WebSocket的通訊原理、協議格式、安全性》
《 微信小程序中如何使用WebSocket實現長鏈接(含完整源碼)》
《 八問WebSocket協議:爲你快速解答WebSocket熱門疑問》
《 快速瞭解Electron:新一代基於Web的跨平臺桌面技術》
《 一文讀懂前端技術演進:盤點Web前端20年的技術變遷史》
《 Web端即時通信基礎知識補課:一文搞懂跨域的全部問題!》
《 Web端即時通信實踐乾貨:如何讓你的WebSocket斷網重連更快速?》
>> 更多同類文章 ……
(本文同步發佈於:http://www.52im.net/thread-3098-1-1.html)