目前在做設(shè)備物聯(lián)網(wǎng),分成了客戶端client_gateway
和設(shè)備端device_gateway
,因為設(shè)備電池比較小,硬件大佬要做低功耗,就說服老板取消了定時發(fā)送心跳,搞的現(xiàn)在設(shè)備在線狀態(tài)都不準確了。
今天想起可以反向發(fā)送心跳檢測,就試了下,確實發(fā)出去了,但是也出現(xiàn)了新問題:
設(shè)備不再發(fā)送心跳,為了省電(我也不知道究竟能省多少電...)
客戶端(小程序)模式不變定時發(fā)送心跳給服務(wù)器
device_gateway
增加了服務(wù)端發(fā)送心跳
// 服務(wù)端定時向客戶端(設(shè)備)發(fā)送的數(shù)據(jù)(在設(shè)備不發(fā)心跳的情況下)
if (empty($GatewayDevicePingCloseLimit)) {
$gateway->pingData = 'ping';
$gateway->pingInterval = 20;
}
經(jīng)過測試設(shè)備可以收到ping
,但是當我把設(shè)備斷電后等了好久,似乎并沒有執(zhí)行關(guān)閉通道操作
/**
* 當斷開連接時觸發(fā)
* @param int $connect_id 連接id
*/
public static function onClose($connect_id)
{
$target = 'client';
//設(shè)備下線
if (isset($_SESSION['device_info'])) {
$target = 'device';
//出現(xiàn) not_log 時不進行下線處理,一般用于重復(fù)連接時關(guān)閉舊連接的情況
if (!isset($_SESSION['not_log'])) {
$device_info = $_SESSION['device_info'];
//生成設(shè)備下線日志,同時改變設(shè)備狀態(tài)
self::$service->create_device_log($device_info['device_id'], 4, '設(shè)備下線了');
//發(fā)送給客戶端(用戶)
self::send_client_message_by_user_id($device_info['user_id'], 'device_online_state', 0, $device_info);
} else {
unset($_SESSION['not_log']);
}
unset($_SESSION['device_info']);
}
//這里只是打印消息日志
self::print_client_message($connect_id, 'onClose', $target, 'down');
}
加個not_log
標識是為了解決踢掉舊鏈接時把狀態(tài)改為離線的bug
求大佬看看哪里的問題
TCP機制就是這樣,斷電這種無法及時檢測到,要等數(shù)據(jù)包不斷重傳超時后才能發(fā)現(xiàn)連接斷開,這個時間很久。
修改linux內(nèi)核,開啟keepalive,修改keepalive時間和間隔好像可以緩解這種情況
可是官方文檔是這么說的由于心跳是周期性檢測,實際執(zhí)行onClose的時間一般會大于pingInterval*pingNotResponseLimit=55,誤差在pingInterval內(nèi)。
http://www.wtbis.cn/doc/gateway-worker/heartbeat.html
官方說的沒問題啊,官方說的這個前提是客戶端有發(fā)心跳或響應(yīng)心跳,并且服務(wù)端設(shè)置了pingNotResponseLimit。你的客戶端不發(fā)心跳,也不響應(yīng)心跳,也沒辦法設(shè)置pingNotResponseLimit,根本不符合這塊文檔的條件
那應(yīng)該如何判斷設(shè)備是否在線呢?沒有執(zhí)行關(guān)閉 connect_id 就一直存在,沒辦法判斷啊,難道判斷最后一次發(fā)送數(shù)據(jù)的時間么
設(shè)備取消心跳了,只能記錄最后一次上報數(shù)據(jù)的時間,然后定時做超時對比了,只是時間太短還不如用心跳,時間太長狀態(tài)也不準確,硬件大佬真是坑爹啊