chat程序中addClientToRoom带来的疑问

allovine

在chat程序中,addClientToRoom有一个// 获取所有所有房间的实际在线客户端列表,以便将存储中不在线用户删除
$all_online_client_id = Gateway::getOnlineStatus(); 这句话的调用,不知道为何这么做?
难道心跳消息检测失败,服务器不会抛出onClose事件?
如果这句话在每次新用户加入聊天时调用,性能实在有些太差了,并且获取时也没有room_id参数,获取所有在线用户。

谢谢。

2258 5 0
5个回答

allovine

另外对于房间里面用户列表的Store,如果是redis的话,应该是可以把 $handler = fopen(__FILE__, 'r');
flock($handler, LOCK_EX); 类似这样的代码删除掉的吧

  • 暂无评论
allovine

我用手机测试了下,好像不会自动移出。

后台日志:

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的规则?

  • 暂无评论
allovine
/**
 * 心跳逻辑
 * @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 里面加上显示的调用?

  • 暂无评论
walkor

// 获取所有房间的实际在线客户端列表,以便将存储中不在线用户删除
$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
不管是谁断开,只要服务端检测到连接已经断开,就会触发

另外对于房间里面用户列表的Store,如果是redis的话,应该是可以把 $handler = fopen(__FILE__, 'r');
flock($handler, LOCK_EX); 类似这样的代码删除掉的吧

不能简单的直接去掉锁,读取列表+保存列表并非原子操作,多进程同时读取+保存时会有并发问题,当然你可以利用redis提供的接口保证原子性,那样可以去掉锁

  • 暂无评论
allovine

非常感谢群主这么及时的回复,真是让我对workerman相当尊敬

  • 暂无评论
年代过于久远,无法发表回答
🔝