Swoole webSocket客服IM消息系统方案解析
作者:stark张宇 发布时间:2023-06-14 20:17:22
概述
基于Swoole的websocket服务,再之前的消息系统系列的第4篇,实现了更加复杂的业务场景,是对消息推送的完善和优化,代码本身就是不断自我优化的过程。
实现方案
技术的实现方案点主要PMQ,2组客户端(用户端、客服管理端),3个主要的部分组成(Push推送消息+Pull拉取未读消息+MessageQueue消息队列),具体流程和交互方式见上面的架构流程图。
1.建立链接,借鉴Tcp3次握手的原理,将每一次的用户询问新增一个关系,询问结束时再将关系释放,因为每次随机分配的客服是不一致的,客服管理员控制台,进入控制台会触发检测客服映射关系的程序,以保证关系的唯一性。
2.客服分配:客服分配是根据用户是否为第一次进入链接进行判定依据,首次会随机分发配给在线客服中的其中一个,如果之前分配过的客服也在线,优化分配存在客服,这样处理的原因是客服不易变,用户异变,防止反复链接/断开操作,减少网络开销。
3.并发锁:相同用户在同一时间有3s的锁定状态,用来防止关系错乱,在客户端发来请求时优先获取缓存,近少可能的访问数据库,提高服务的稳定性和性能。
//设置分布式锁,3s之内只能请求一次
$lock = RedisPool::invoke(function (Redis $redis) use ($toUid) {
return $redis->get(Category::$openLock . $toUid);
}, self::REDIS_CONN_NAME);
if ($lock) {
$msgErrorRet['code'] = 416;
$msgErrorRet['msg'] = 'Please try again';
return $this->response()->setMessage(json_encode($msgErrorRet));
}
//查询是否存在链接关系
$imUserRelation = RedisPool::invoke(function (Redis $redis) use ($toUid) {
$redis->setEx(Category::$openLockPrefix . $toUid, 3, $toUid);
return $redis->get(Category::$imUserRelationName . $toUid);
}, self::REDIS_CONN_NAME);
4.网络异常处理,回收服务:针对App崩溃、网络异常断开的链接,主动监听断开的fd,进行关系处理,对所有断开链接的websocket,进行回收,清除关系。
static function onClose(\swoole_server $server, int $fd, int $reactorId)
{
$info = $server->getClientInfo($fd);
$fd = intval($fd);
if ($info && $info['websocket_status'] === WEBSOCKET_STATUS_FRAME) {
TaskManager::getInstance()->async(function () use ($fd) {
RedisPool::invoke(function (Redis $redis) use ($fd) {
//回收用户
$uid = $redis->hGet('PUSH_MSG_SOCKET_FD', $fd);
if (isset($uid) && !empty($uid) && is_numeric($uid)) {
$redis->zRem('PUSH_MSG_USER_LOGIN', $fd);
//检测是否有客服关系未断开
$redis->del(Category::$imUserRelationName . $uid);
$redis->hDel('PUSH_MSG_SOCKET_FD', $fd);
}
//回收客服管理用户
$cUid = $redis->hGet('PUSH_CUSTOMER_MSG_SOCKET_FD', $fd);
if (isset($cUid) && !empty($cUid)) {
$redis->zRem('PUSH_CUSTOMER_MSG_USER_LOGIN', $fd);
$redis->hDel('PUSH_CUSTOMER_MSG_SOCKET_FD', $fd);
}
}, 'redis');
});
}
}
5.获取离线消息分配算法,按照客服管理员在线人数,把离线消息按照用户来重新组装,平均分配给在线管理员,如果数量不能被整除,也不会造成分配不均情况。
//验证客服管理员在线
$vUid = [];
$server = ServerManager::getInstance()->getSwooleServer();
foreach ($virtualUid as $fd => $vid){
$info = $server->getClientInfo($fd);
if ($info && $info['websocket_status'] == 3) {
$vUid[$fd] = $vid;
}
}
if (!empty($pullData) && !empty($vUid)) {
$uIds = array_keys($pullData);
$row = ceil(count($uIds) / count($vUid));
$share = array_chunk($uIds, $row, true);
$keyDict = $vUid;
$pushList = [];
// code 组装代码略...
}
6.websocket对象不回收:从控制台打开新窗口时,就会新增一个websocket对象,后来在浏览器中刷新处理的,没有找到回收的办法。
7.心跳:客服的websocket心跳使用的是实时push消息,5s循环一次,防止链接断开,服务下线。
实践Swoole里的坑
链接数变化正常,但是内存好像没有得到很好的释放,而且进程里也出现了很多野进程,野进程多可能存在的原因是这样的,你没有守护启动,然后主进程挂了,后面的进程找不到父进程,变成了僵尸进程或者是孤儿进程。
内存也不对劲,大概率是我执行脚本里出了问题,去掉了修改配置的语句,在Base类里加入了unset,及时释放掉内存。
出现问题的解决的原因是我在Crontab脚本里加了结束时长造成的问题,cli模式下的php生命周期进程得不到释放造成的,合理使用Swoole中的协程就好了。
ini_set('memory_limit', '1024M');
set_time_limit(0);
成果
上线2年的时间里,进行了5次升级和优化,活跃用户10w+,最高峰值6w/s,130w/h访问量,是一个非常成功的实践结果。
用最简单的技术实现方式,节省企业成本,减少系统开发和维护成本,提高办公效率才是技术人应该做的事儿,做解决实际复杂业务解决方案并落地的技术人,En。
来源:https://juejin.cn/post/7209447968217743416
猜你喜欢
- blankzheng的blog:http://www.planabc.net/margin在中文中我们翻译成外边距或者外补白(本文中引用外边
- 在asp里通过以下两个函数实现javascript里的escape函数和unescape函数加密功能。在ajax post或get时内容存在
- 一般情况下会有几种情况需要你把数据库设为只读: 1. Insert,Update,Delete 触发器 2. Check 约束 和 Dele
- 在设计网页之前,客户或产品经理会提出对网页视觉风格设计的期望:活跃、大气、稳重、信赖、都市化….. 设计师一听到关键词或许很自然地在心里蹦出
- 1.函数array() 功能:创建一个数组变量 格式:array(list) 参数:list为数组变量中的每个数值列,中间用逗号间隔 例子:
- * 对子查询和Join进行了优化,包括对MyISAD和InnoB存储引擎分散范围内的批量索引访问。* 增加了 BACKUP DATABASE
- 问题:如果一个网站拥有两个域名:domain1.com和domain2.com。在网站运营前期,主推domain1.com,但发展到中期,由
- 求3721,163,1,4832,1980,2008,68686688,9999,17173,5173,8848中最大的数明白后,试着求一下
- 从最基础的说起。本教程中,所有IE 均指 WindowXP + IE 6.0, 所有 FF 均指 FF 1.5。1. 不用编程的部分1.1
- 本讲的内容是使用ASP的ActiveX Server Components(组件),说实话下面的内置组件我们用的很少。一、 Browser
- 在应用SA-FileUp时,必须确认用户已对目的路径有读、写、删除的权力。在多文件上传中,由于浏览器不支持SIZE= 属性,所以对多文件的情
- 如何阻止别人非法链接你网站的图片,防盗链?getimage.asp<% Option ExplicitDim&nb
- Access method(访问方法):此步骤包括从文件中存储和检索记录。Alias(别名):某属性的另一个名字。在SQL中,可以用别名替换
- 1、善用css缩写可以减少页面文件大小,提高下载速度,同时使代码简洁可读。代码:div{ b
- 在asp编程中,我们常常使用trim(rtrim ,ltrim)函数去掉一些数据的开头和结尾的空格,笔者最近写了一个asp聊天室,看到下面的
- 本人曾经用过的备份方式有:mysqldump、mysqlhotcopy、BACKUP TABLE 、SELECT INTO OUTFILE,
- 在许多用SQL Server实现的新的企业系统设计中,系统设计师需要在给数据结构和管理应用程序逻辑的定位上做出具有关键性意义的决定。SQL
- 最近在着手支付宝个人版改版的项目,正好在一些国内知名的SNS网站上分别注册了帐户进行体验。显然一点,国内的SNS都带有Facebook的影子
- 在给客户做个程序时,突然遇到个问题,就是产品页用户提交视频播放文件时,如何根据提交的网址内的视频格式进行正确的播放呢....郁闷了一会,想好
- 在SQL Server中进行开发会让你身处险地,并且寻找快速解决方案。我们编辑了前十名关于SQL Server开发的常见问题。对常见的针对表