1,最近在做一个实时监控项目,用到了html5的websocket。场景如下:
点击设备列表,进入某个设备详情页面,监控这个设备的运行状况,实时更新一些设备运行状态参数。
2,前端代码如下:
// 省略...
const socket = new WebSocket(server);
socket.addEventListener('open', function(e) {
socket.send(该设备id);
});
socket.addEventListener('message', function(e) {
const data = e.data;
更新设备信息(data);
});
socket.addEventListener('close', function(e) {
console.log('websockt连接已关闭');
});
socket.addEventListener('error', function(e) {
alert("websockt连接发生错误,请刷新页面重试!");
});
// 省略...
3,后端我用的php的wokerman(一个php sokcet服务框架)提供的一个websocket服务用来测试(我目前做前端开发,用这个workerman只是用来测试)。
// 心跳间隔25秒
define('HEARTBEAT_TIME', 25);
$ws_worker = new Worker("websocket://0.0.0.0:9668");
// 8 processes
$ws_worker->count = 8;
// 进程启动后设置一个每秒运行一次的定时器
$ws_worker->onWorkerStart = function($worker) {
Timer::add(1, function()use($worker){
$time_now = time();
foreach($worker->connections as $connection) {
// 有可能该connection还没收到过消息,则lastMessageTime设置为当前时间
if (empty($connection->lastMessageTime)) {
$connection->lastMessageTime = $time_now;
continue;
}
// 上次通讯时间间隔大于心跳间隔,则认为客户端已经下线,关闭连接
if ($time_now - $connection->lastMessageTime > HEARTBEAT_TIME) {
$connection->close();
}
}
});
};
// Emitted when data received
$ws_worker->onMessage = function ($connection, $data) {
// 给connection临时设置一个lastMessageTime属性,用来记录上次收到消息的时间
$connection->lastMessageTime = time();
while (true) {
$connection->send(根据接收到的客户端的$data发送要发送的消息);
usleep(1000000); // 睡1秒
}
};
4,现象:
我发现如果刷新设备详情页面8次以上(这个次数正好和后端代码中设置的wokerman的进程数相等),客户端就会连接不上后端的socket服务,一直pending,直到提示:WebSocket opening handshake timed out。而wokerman的运行状态则为:
5,问题:
我想问下我的代码哪里出了问题,是前端js代码或业务逻辑有问题还是后端php代码或业务有问题。
问题解决了,workerman论坛上的一个哥们提醒了我是死循环导致进程一直处于busy状态。把php代码中的死循环去掉:
前端代码中增加定时向后端发送消息的代码,这样后端就可以根据监听到的前端发送的消息往前端推送消息。worker进程只有在发送消息时才会处于busy状态,否则就会idle。之前就是因为死循环一直发送消息(busy),这样就导致前端页面每次加载都会导致后端创建一个新的进程(原来的worker进程一直没释放)。我之前对后端往前端推送消息的业务逻辑和技术细节没想清楚,哎,自己给自己挖了个坑。