為什么需要心跳檢測?
正常的情況客戶端斷開連接會向服務(wù)端發(fā)送一個fin包,服務(wù)端收到fin包后得知客戶端連接斷開,則立刻觸發(fā)onClose事件回調(diào)。
但是有些極端情況如客戶端掉電、網(wǎng)絡(luò)關(guān)閉、拔網(wǎng)線、路由故障等,這些極端情況客戶端無法發(fā)送fin包給服務(wù)端,服務(wù)端便無法知道連接已經(jīng)斷開。如果客戶端與服務(wù)端定時有心跳數(shù)據(jù)傳輸,則會比較及時的發(fā)現(xiàn)連接斷開,觸發(fā)onClose事件回調(diào)。
另外路由節(jié)點防火墻會關(guān)閉長時間不通訊的socket連接,導(dǎo)致socket長連接斷開。所以需要客戶端與服務(wù)端定時發(fā)送心跳數(shù)據(jù)保持連接不被斷開。
心跳檢測的原理是什么?
客戶端定時每X秒(推薦小于60秒)向服務(wù)端發(fā)送特定數(shù)據(jù)(任意數(shù)據(jù)都可),服務(wù)端設(shè)定為X秒沒有收到客戶端心跳則認為客戶端掉線,并關(guān)閉連接觸發(fā)onClose回調(diào)。這樣即通過心跳檢測請求維持了連接(避免連接因長時間不活躍而被網(wǎng)關(guān)防火墻關(guān)閉),也能讓服務(wù)端比較及時的知道客戶端是否異常掉線。
GatewayWorker中如何配置心跳檢測?
目前GatewayWorker支持兩種心跳檢測,服務(wù)端設(shè)定多少秒內(nèi)沒收到心跳關(guān)閉連接,同時也支持服務(wù)端定時向客戶端發(fā)送心跳數(shù)據(jù),推薦客戶端發(fā)送心跳,也可以兩種檢測同時開啟。
客戶端定時發(fā)送心跳(推薦)
客戶端定時向服務(wù)端發(fā)送心跳。服務(wù)端類似以下配置:
$gateway = new Gateway("Websocket://0.0.0.0:8585");
$gateway->pingInterval = 55;
$gateway->pingNotResponseLimit = 1;
$gateway->pingData = '';
以上配置含義是客戶端連接 pingInterval*pingNotResponseLimit=55
秒內(nèi)沒有任何數(shù)據(jù)傳輸給服務(wù)端則服務(wù)端認為對應(yīng)客戶端已經(jīng)掉線,服務(wù)端關(guān)閉連接并觸發(fā)onClose回調(diào)。
由于心跳是周期性檢測,實際執(zhí)行onClose的時間一般會大于
pingInterval*pingNotResponseLimit=55
,誤差在pingInterval
內(nèi)。
客戶端的心跳周期應(yīng)該要比服務(wù)端設(shè)置的$gateway->pingInterval=55
小一些,例如客戶端50秒發(fā)送一段數(shù)給服務(wù)端
服務(wù)端主動發(fā)送心跳
GatewayWorer支持服務(wù)端向客戶端發(fā)送心跳檢測,可以像下面這樣設(shè)置。
$gateway = new Gateway("Websocket://0.0.0.0:8585");
$gateway->pingInterval = 55;
$gateway->pingNotResponseLimit = 0;
// 服務(wù)端定時向客戶端發(fā)送的數(shù)據(jù)
$gateway->pingData = '{"type":"ping"}';
以上服務(wù)端會定時55秒給客戶端發(fā)心跳數(shù)據(jù){"type":"ping"}
,而客戶端不需要定時向服務(wù)端發(fā)送心跳數(shù)據(jù)。
其中pingNotResponseLimit = 0
代表服務(wù)端允許客戶端不發(fā)送心跳,服務(wù)端不會因為客戶端長時間沒發(fā)送數(shù)據(jù)而斷開連接。
如果pingNotResponseLimit = 1
,則代表客戶端必須定時發(fā)送數(shù)據(jù)給服務(wù)端,否則pingNotResponseLimit*pingInterval=55
秒內(nèi)沒有任何數(shù)據(jù)發(fā)來則關(guān)閉對應(yīng)連接,并觸發(fā)onClose。
由于心跳是周期性檢測,實際執(zhí)行onClose的時間一般會大于
pingInterval*pingNotResponseLimit=55
,誤差在pingInterval
內(nèi)。
說明:
Gateway::$pingInterval
心跳檢測時間間隔 單位:秒。如果設(shè)置為0代表不做任何心跳檢測。
Gateway::$pingNotResponseLimit
客戶端連續(xù)$pingNotResponseLimit
次$pingInterval
時間內(nèi)不發(fā)送任何數(shù)據(jù)(包括但不限于心跳數(shù)據(jù))則斷開鏈接,并觸發(fā)onClose。
如果設(shè)置為0代表客戶端不用發(fā)送心跳數(shù)據(jù),即通過TCP層面檢測連接的連通性(極端情況至少10分鐘才能檢測到連接斷開,甚至可能永遠檢測不到)
Gateway::$pingData
當(dāng)需要服務(wù)端定時給客戶端發(fā)送心跳數(shù)據(jù)時, $gateway->pingData
設(shè)置為服務(wù)端要發(fā)送的心跳請求數(shù)據(jù),心跳數(shù)據(jù)是任意的,只要客戶端能識別即可??蛻舳耸盏叫奶鴶?shù)據(jù)可以忽略不做任何處理。
注意:
當(dāng)設(shè)置為服務(wù)端主動發(fā)送心跳時,心跳間隔并不是100%精準(zhǔn)。當(dāng)客戶端連接成功后,服務(wù)端發(fā)來的第一個心跳的時間間隔可能要小于服務(wù)器設(shè)置的值。
當(dāng)設(shè)置為服務(wù)端主動發(fā)送心跳時,如果客戶端最近有發(fā)來數(shù)據(jù),那么證明客戶端存活,服務(wù)端會省略一個心跳,下個心跳大約1.5*$gateway->pingInterval
秒后發(fā)送。
如果心跳是客戶端發(fā)送,$gateway->pingNotResponseLimit最好大于0,這樣可以及時檢測到一些死連接(連接已經(jīng)斷開,但是服務(wù)端不知道)
斷線重連(重要)
不管是客戶端發(fā)送心跳還是服務(wù)端發(fā)送心跳,連接都有斷開的可能。例如瀏覽器最小化js被暫停、瀏覽器切換到其它tab頁面js被暫停、電腦進入睡眠等等、移動端切換網(wǎng)絡(luò)、信號變?nèi)?、手機黑屏、手機應(yīng)用切換到后臺、路由故障、業(yè)務(wù)主動斷開等。尤其是外網(wǎng)環(huán)境復(fù)雜,很多路由節(jié)點會清理1分鐘內(nèi)不活躍的連接,這也是為什么心跳間隔推薦小于1分鐘的原因。
連接在外網(wǎng)環(huán)境很容易被斷開,所以斷線重連是長連接應(yīng)用必須具備的功能(斷線重連只能客戶端做,服務(wù)端無法實現(xiàn))。例如瀏覽器websocket需要監(jiān)聽onclose事件,當(dāng)發(fā)生onclose時建立新的連接(為避免需崩可延建立連接)。更嚴(yán)格一點,服務(wù)端也應(yīng)該定時發(fā)起心跳數(shù)據(jù),并且客戶端需要定時監(jiān)測服務(wù)端的心跳數(shù)據(jù)是否超時,超過規(guī)定時間未收到服務(wù)端心跳數(shù)據(jù)應(yīng)該認定連接已經(jīng)斷開,需要執(zhí)行close關(guān)閉連接,并重新建立新的連接。