1. 心跳重連原由

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