1. 心跳重連原由
心跳和重連的目的用一句話概括就是客戶端和服務端保證彼此還活着,避免丟包發生。
websocket
連接斷開有以下兩證情況:
前端斷開
在使用websocket
過程中,可能會出現網絡斷開的情況,比如信號不好,或者網絡臨時關閉,這時候websocket的連接已經斷開,而不同瀏覽器有不同的機制,觸發onclose
的時機也不同,並不會理想執行websocket
的onclose
方法,我們無法知道是否斷開連接,也就無法進行重連操作。
后端斷開
如果后端因為一些情況需要斷開ws,在可控情況下,會下發一個斷連的消息通知,之后才會斷開,我們便會重連。
如果因為一些異常斷開了連接,我們是不會感應到的,所以如果我們發送了心跳一定時間之后,后端既沒有返回心跳響應消息,前端又沒有收到任何其他消息的話,我們就能斷定后端主動斷開了。
因此需要一種機制來檢測客戶端和服務端是否處於正常連接的狀態。通過在指定時間間隔發送心跳包來保證連接正常,如果連接出現問題,就需要手動觸發onclose
事件,這時候便可進行重連操作。因此websocket
心跳重連就應運而生。
2. 心跳重連的簡單實現
2.1 通過createWebSocket創建連接
function createWebSocket() { try { ws = new WebSocket(wsUrl); init(); } catch(e) { console.log('catch'); reconnect(wsUrl); } }
2.2 創建init方法,初始化一些監聽事件,如果希望websocket連接一直保持, 我們會在close或者error上綁定重新連接方法。
function init() { ws.onclose = function () { console.log('鏈接關閉'); reconnect(wsUrl); }; ws.onerror = function() { console.log('發生異常了'); reconnect(wsUrl); }; ws.onopen = function () { //心跳檢測重置 heartCheck.start(); }; ws.onmessage = function (event) { console.log('接收到消息'); //拿到任何消息都說明當前連接是正常的 heartCheck.start(); } }
2.3 重連操作,通過設置lockReconnect
變量避免重復連接
var lockReconnect = false;//避免重復連接 function reconnect(url) { if(lockReconnect) { return; }; lockReconnect = true; //沒連接上會一直重連,設置延遲避免請求過多 tt && clearTimeout(tt); tt = setTimeout(function () { createWebSocket(url); lockReconnect = false; }, 4000); }
2.4 心跳檢測
//心跳檢測 var heartCheck = { timeout: 3000, //每隔三秒發送心跳 severTimeout: 5000, //服務端超時時間 timeoutObj: null, serverTimeoutObj: null, start: function(){ var _this = this; this.timeoutObj && clearTimeout(this.timeoutObj); this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj); this.timeoutObj = setTimeout(function(){ //這里發送一個心跳,后端收到后,返回一個心跳消息, //onmessage拿到返回的心跳就說明連接正常 ws.send("123456789"); // 心跳包 //計算答復的超時時間 _this.serverTimeoutObj = setTimeout(function() { ws.close(); }, _this.severTimeout); }, this.timeout) } }
有的時候,客戶端發送3次心跳包服務端均未回復才判定為失去連接,所以這時需要加上計數來判斷。
//心跳檢測 var heartCheck = { timeout: 3000, //每隔三秒發送心跳 num: 3, //3次心跳均未響應重連 timeoutObj: null, serverTimeoutObj: null, start: function(){ var _this = this; var _num = this.num; this.timeoutObj && clearTimeout(this.timeoutObj); this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj); this.timeoutObj = setTimeout(function(){ //這里發送一個心跳,后端收到后,返回一個心跳消息, //onmessage拿到返回的心跳就說明連接正常 ws.send("123456789"); // 心跳包 _num--; //計算答復的超時次數 if(_num === 0) { ws.colse(); } }, this.timeout) } }
最后總結下
我們確認了后端單台服務器的處理能力有限,因此。我們需要做集群。其次我們為了不讓前端關閉或回收,后端不響應。我們需要設置心跳,定時清除無關的連接。
最后,我們需要有消息確認機制,做到保證消息的100%接收。