目前在做设备物联网,分成了客户端client_gateway
和设备端device_gateway
,因为设备电池比较小,硬件大佬要做低功耗,就说服老板取消了定时发送心跳,搞的现在设备在线状态都不准确了。
今天想起可以反向发送心跳检测,就试了下,确实发出去了,但是也出现了新问题:
设备不再发送心跳,为了省电(我也不知道究竟能省多少电...)
客户端(小程序)模式不变定时发送心跳给服务器
device_gateway
增加了服务端发送心跳
// 服务端定时向客户端(设备)发送的数据(在设备不发心跳的情况下)
if (empty($GatewayDevicePingCloseLimit)) {
$gateway->pingData = 'ping';
$gateway->pingInterval = 20;
}
经过测试设备可以收到ping
,但是当我把设备断电后等了好久,似乎并没有执行关闭通道操作
/**
* 当断开连接时触发
* @param int $connect_id 连接id
*/
public static function onClose($connect_id)
{
$target = 'client';
//设备下线
if (isset($_SESSION['device_info'])) {
$target = 'device';
//出现 not_log 时不进行下线处理,一般用于重复连接时关闭旧连接的情况
if (!isset($_SESSION['not_log'])) {
$device_info = $_SESSION['device_info'];
//生成设备下线日志,同时改变设备状态
self::$service->create_device_log($device_info['device_id'], 4, '设备下线了');
//发送给客户端(用户)
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
标识是为了解决踢掉旧链接时把状态改为离线的bug
求大佬看看哪里的问题
TCP机制就是这样,断电这种无法及时检测到,要等数据包不断重传超时后才能发现连接断开,这个时间很久。
修改linux内核,开启keepalive,修改keepalive时间和间隔好像可以缓解这种情况
可是官方文档是这么说的
由于心跳是周期性检测,实际执行onClose的时间一般会大于pingInterval*pingNotResponseLimit=55,误差在pingInterval内。
https://www.workerman.net/doc/gateway-worker/heartbeat.html官方说的没问题啊,官方说的这个前提是客户端有发心跳或响应心跳,并且服务端设置了pingNotResponseLimit。你的客户端不发心跳,也不响应心跳,也没办法设置pingNotResponseLimit,根本不符合这块文档的条件
那不是没法解决了,哎,愁死了,感谢大佬耐心解答
这个断电问题,你看下能不能在服务端加个定时器,隔一段时间扫描一下在线设备。
定时全部扫描一遍?还是每个连接单独一个定时器,这样确实能解决,我也想过,就是怕影响稳定性
就写在一个进程就好了。
那应该如何判断设备是否在线呢?没有执行关闭 connect_id 就一直存在,没办法判断啊,难道判断最后一次发送数据的时间么
在登录的时候还是要入一下库,例如redis集合吧。然后扫描在线列表(扫到心跳T掉客户端的时候)求差集,就是你想要得异常下线的吧?
或者在客户端ping的时候,用session记录ping的时间,然后下次扫的时候,用和服务器心跳一样的算法判断他是否掉线。
设备取消心跳了,只能记录最后一次上报数据的时间,然后定时做超时对比了,只是时间太短还不如用心跳,时间太长状态也不准确,硬件大佬真是坑爹啊
极端情况只能这样了,百分之98的场景能通就行呀
最后怎么解决的呀
定时器上报吧 未上报的就异常