Swoole两种方案实现总后台给所有在线用户 群发通知
2020-03-07 本文已影响0人
骑蚂蚁上高速_jun
场景:
公司的CRM 客户管理系统,由于需要在总后台,在线给在线用户推送消息(如:版本升级提示在线用户刷新页面,在线用户优惠方案等等之类的,使用场景还是比较多的),下面我就 两种实现思路和解决方案:
方案一:
思路 : 使用 manager 中的用户进程 addProcess 结合redis 阻塞消息队列实现
优点 : 代码实现简单比第二种更简单,性能也是最高
缺点 : 不支持热重起 reload, 由于用户自定义进程属于 manager 进程 fork出来的,所以不支持reload。还多加一个redis客户端
$server = new Server("0.0.0.0",20001); // webscoket服务器
Runtime::enableCoroutine(SWOOLE_HOOK_ALL | SWOOLE_HOOK_CURL);
.... 启动加载各种事件,配置等等代码在此省略
$server->addProcess(new Process(function(Process $process)use($server){
co::create(function()use($server){
$redis = new redis();
$redis->connect("127.0.0.1");
$redis->setOption(redis::OPT_READ_TIMEOUT,-1);
$data = $redis->brPop("list:swoole",0); // 阻塞等待用户信息进入
// 遍历所有的在线客户端连接
foreach ($server->connections as $fd){
if($server->isEstablished($fd)){
// 将消息内容推送到客户端
$server->push($fd,$data[1]);
}
}
});
}));
$server->start();
方案二:
思路:在websocket中的 on Message 事件中,根据消息判断数据是否为总后台管理员需要给系统所有用户推送广播通知。
实际复杂项目中,推荐这种,因为支持热重起实在是太重要了
优点: 支持热重启 reload, set配置好后,支持原生协程。
缺点: 性能稍微差,代码稍微复杂
// 接收到客户端消息触发
public function message(Server $server,Swoole\WebSocket\Frame $frame){
$fd = $frame->fd;
echo "接收到客户端{$fd}数据:".$frame->data ."\n";
$std = @json_decode($frame->data);
if($std){
// 判断消息是否为总后台发送广播的密钥 broadcast secret 一定不能暴露
if($std->type=="broadcast secret"){
foreach ($server->connections as $clientFd){
// 排除给总后台自己发送广播
if($fd != $clientFd && $server->isEstablished($clientFd)){
echo "推送给客户端\n";
$server->push($clientFd,$std->content);
}
}
}
}else{
// 不是合理的json 认为是非法客户端
$server->close($fd);
return false;
}
}