在chat程序中,addClientToRoom有一个// 获取所有所有房间的实际在线客户端列表,以便将存储中不在线用户删除 $all_online_client_id = Gateway::getOnlineStatus(); 这句话的调用,不知道为何这么做? 难道心跳消息检测失败,服务器不会抛出onClose事件? 如果这句话在每次新用户加入聊天时调用,性能实在有些太差了,并且获取时也没有room_id参数,获取所有在线用户。
谢谢。
另外对于房间里面用户列表的Store,如果是redis的话,应该是可以把 $handler = fopen(__FILE__, 'r'); flock($handler, LOCK_EX); 类似这样的代码删除掉的吧
我用手机测试了下,好像不会自动移出。
后台日志:
client:192.168.1.100:58876 gateway:127.0.0.1:3008 client_id:8 session:{"room_id":1,"client_name":"ooo"} onMessage:{"type":"pong"} client:127.0.0.1:64599 gateway:127.0.0.1:3005 client_id:6 session:{"room_id":1,"client_name":"12345"} onMessage:{"type":"say","to_client_id":"all","to_client_name":"所有人","content":",密密麻麻"} client:127.0.0.1:64599 gateway:127.0.0.1:3005 client_id:6 session:{"room_id":1,"client_name":"12345"} onMessage:{"type":"pong"} client:192.168.1.100:58876 gateway:127.0.0.1:3008 client_id:8 session:{"room_id":1,"client_name":"ooo"} onMessage:{"type":"pong"} client:127.0.0.1:64599 gateway:127.0.0.1:3005 client_id:6 session:{"room_id":1,"client_name":"12345"} onMessage:{"type":"pong"} client:192.168.1.100:58876 gateway:127.0.0.1:3008 client_id:8 session:{"room_id":1,"client_name":"ooo"} onMessage:{"type":"pong"} ========我把手机的wifi关掉,在这里之后,就没有再给 client_id_8 发出心跳消息了 client:127.0.0.1:64599 gateway:127.0.0.1:3005 client_id:6 session:{"room_id":1,"client_name":"12345"} onMessage:{"type":"pong"} client:127.0.0.1:64599 gateway:127.0.0.1:3005 client_id:6 session:{"room_id":1,"client_name":"12345"} onMessage:{"type":"pong"} client:127.0.0.1:64599 gateway:127.0.0.1:3005 client_id:6 session:{"room_id":1,"client_name":"12345"} onMessage:{"type":"pong"} client:127.0.0.1:64599 gateway:127.0.0.1:3005 client_id:6 session:{"room_id":1,"client_name":"12345"} onMessage:{"type":"pong"}
redis:
get ROOM_CLIENT_LIST-1 "a:2:{i:6;s:5:\"12345\";i:8;s:3:\"ooo\";}"
=========等了一会,手机锁屏了,然后终于有了
client:192.168.1.100:58876 gateway:127.0.0.1:3008 client_id:8 onClose:'' 调用 redis也正常了
=========再次测试,手机不锁屏
client:192.168.1.100:58913 gateway:127.0.0.1:3006 client_id:9 session:null onMessage:{"type":"re_login","client_name":"ooo","room_id":1} client:127.0.0.1:64599 gateway:127.0.0.1:3005 client_id:6 session:{"room_id":1,"client_name":"12345"} onMessage:{"type":"pong"} client:192.168.1.100:58913 gateway:127.0.0.1:3006 client_id:9 session:{"room_id":1,"client_name":"ooo"} onMessage:{"type":"pong"} client:127.0.0.1:64599 gateway:127.0.0.1:3005 client_id:6 session:{"room_id":1,"client_name":"12345"} onMessage:{"type":"pong"} client:127.0.0.1:64599 gateway:127.0.0.1:3005 client_id:6 session:{"room_id":1,"client_name":"12345"} onMessage:{"type":"pong"} client:127.0.0.1:64599 gateway:127.0.0.1:3005 client_id:6 session:{"room_id":1,"client_name":"12345"} onMessage:{"type":"pong"} client:127.0.0.1:64599 gateway:127.0.0.1:3005 client_id:6 session:{"room_id":1,"client_name":"12345"} onMessage:{"type":"pong"} client:127.0.0.1:64599 gateway:127.0.0.1:3005 client_id:6 session:{"room_id":1,"client_name":"12345"} onMessage:{"type":"pong"} client:127.0.0.1:64599 gateway:127.0.0.1:3005 client_id:6 session:{"room_id":1,"client_name":"12345"} onMessage:{"type":"pong"} client:127.0.0.1:64599 gateway:127.0.0.1:3005 client_id:6 session:{"room_id":1,"client_name":"12345"} onMessage:{"type":"pong"} worker exit with status 11 =========日志里面有了句这个,但是一直没看到 onClose 调用 client:127.0.0.1:64599 gateway:127.0.0.1:3005 client_id:6 session:{"room_id":1,"client_name":"12345"} onMessage:{"type":"pong"} client:127.0.0.1:64599 gateway:127.0.0.1:3005 client_id:6 session:{"room_id":1,"client_name":"12345"} onMessage:{"type":"pong"} client:127.0.0.1:64599 gateway:127.0.0.1:3005 client_id:6 session:{"room_id":1,"client_name":"12345"} onMessage:{"type":"pong"}
=====截止到现在,redis里面这个房间依然是有两个用户,估计后续也不会调用onClose了。
请问群主能否解释下,服务器心跳检测,调用onClose的规则?
/** * 心跳逻辑 * @return void */ public function ping() { // 遍历所有客户端连接 foreach($this->_clientConnections as $connection) { // 上次发送的心跳还没有回复次数大于限定值就断开 if($this->pingNotResponseLimit > 0 && $connection->pingNotResponseCount >= $this->pingNotResponseLimit) { $connection->destroy(); continue; } // $connection->pingNotResponseCount为-1说明最近客户端有发来消息,则不给客户端发送心跳 if($connection->pingNotResponseCount++ >= 0) { if($this->pingData) { $connection->send($this->pingData); } } } }
如果在启动程序 start_gateway 里面加上 $gateway->pingNotResponseLimit = 3; 应该会走到 $connection->destroy();continue; 这里,但是也调用不到Event.php里面的onClose事件处理函数吧,在 ping 里面加上显示的调用?
// 获取所有房间的实际在线客户端列表,以便将存储中不在线用户删除 $all_online_client_id = Gateway::getOnlineStatus(); 这句话的调用,不知道为何这么做?
实际正式部署时不需要这么做, 之所以加了这个,主要是为了开发环境, 开发环境经常要重启服务,重启服务后所有客户端都会断开, 但是存储中的房间用户列表数据还在, 导致数据不一致,所以每个用户登录时都调用了getOnlineStatus, 目的是去掉由于重启服务导致的数据不一致用户。
这里可以优化空间,比如在启动脚本里面加上钩子函数, 服务停止时清空房间用户数据存储(要注意是否有分布式部署,不要把其它机器的用户清除了) 或者用定时器定时清除不在线用户,而不是每次登录都调用getOnlineStatus。
心跳检测参考手册 http://gatewayworker-doc.workerman.net/gateway-worker-development/heartbeat.html 手机关闭网络算是异常断开,和拔网线类似, 客户端不会发送fin包,服务端也就不会立刻检测到网络已经不通, 这种情况需要设置应用层心跳,并且设置pingNotResponseLimit不为0, 可以快速检测这种网络异常情况
onClose 见手册 http://gatewayworker-doc.workerman.net/gateway-worker-development/onclose.html 不管是谁断开,只要服务端检测到连接已经断开,就会触发
锁
不能简单的直接去掉锁,读取列表+保存列表并非原子操作,多进程同时读取+保存时会有并发问题,当然你可以利用redis提供的接口保证原子性,那样可以去掉锁
非常感谢群主这么及时的回复,真是让我对workerman相当尊敬
另外对于房间里面用户列表的Store,如果是redis的话,应该是可以把 $handler = fopen(__FILE__, 'r');
flock($handler, LOCK_EX); 类似这样的代码删除掉的吧
我用手机测试了下,好像不会自动移出。
后台日志:
redis:
=========等了一会,手机锁屏了,然后终于有了
=========再次测试,手机不锁屏
=====截止到现在,redis里面这个房间依然是有两个用户,估计后续也不会调用onClose了。
请问群主能否解释下,服务器心跳检测,调用onClose的规则?
如果在启动程序 start_gateway 里面加上 $gateway->pingNotResponseLimit = 3;
应该会走到
$connection->destroy();continue;
这里,但是也调用不到Event.php里面的onClose事件处理函数吧,在 ping 里面加上显示的调用?
实际正式部署时不需要这么做,
之所以加了这个,主要是为了开发环境,
开发环境经常要重启服务,重启服务后所有客户端都会断开,
但是存储中的房间用户列表数据还在,
导致数据不一致,所以每个用户登录时都调用了getOnlineStatus,
目的是去掉由于重启服务导致的数据不一致用户。
这里可以优化空间,比如在启动脚本里面加上钩子函数,
服务停止时清空房间用户数据存储(要注意是否有分布式部署,不要把其它机器的用户清除了)
或者用定时器定时清除不在线用户,而不是每次登录都调用getOnlineStatus。
心跳检测参考手册
http://gatewayworker-doc.workerman.net/gateway-worker-development/heartbeat.html
手机关闭网络算是异常断开,和拔网线类似,
客户端不会发送fin包,服务端也就不会立刻检测到网络已经不通,
这种情况需要设置应用层心跳,并且设置pingNotResponseLimit不为0,
可以快速检测这种网络异常情况
onClose
见手册
http://gatewayworker-doc.workerman.net/gateway-worker-development/onclose.html
不管是谁断开,只要服务端检测到连接已经断开,就会触发
锁
不能简单的直接去掉锁,读取列表+保存列表并非原子操作,多进程同时读取+保存时会有并发问题,当然你可以利用redis提供的接口保证原子性,那样可以去掉锁
非常感谢群主这么及时的回复,真是让我对workerman相当尊敬