基于Tars高并发IM系统的设计与实现-实战篇5
基于Tars高并发IM系统的设计与实现-实战篇5
群聊服务 GroupChatServer
群聊服务既可以接受来自BrokerServer的用户请求,也需要接收来自其他服务的RPC请求;所以本服务提供两套RPC接口:通用RPC接口和专用RPC接口。
通用RPC接口
通用RPC接口主要处理如下请求:
- 创建群聊
- 群聊加成员
- 群聊减成员
- 修改群资料
- 发群消息
- 换群主
- 解散群聊
- 同步用户群聊
- 获取群成员
- 解散群聊
- 判断一个人是否为群成员
针对以上每个业务,根据用户请求的类型进行不同的业务逻辑处理,处理代码如下:
switch(req.header.type){case otim::PT_MSG_GROUP_CHAT:this->sendMsg(clientContext, req, resp);break;case otim::PT_GROUPCHAT_SYNC:this->syncGroup(clientContext, req, resp);break;case otim::PT_GROUPCHAT_CREATE:this->createGroup(clientContext, req, resp);break;case otim::PT_GROUPCHAT_JION:this->joinGroup(clientContext, req, resp);break;case otim::PT_GROUPCHAT_QUIT:this->quitGroup(clientContext, req, resp);break;case otim::PT_GROUPCHAT_DISMISS:this->dismissGroup(clientContext, req, resp);break;case otim::PT_GROUPCHAT_UPDATE_CREATOR:this->updateGroupCreator(clientContext, req, resp);break;case otim::PT_GROUPCHAT_INFO_UPDATE:this->updateGroupInfo(clientContext, req, resp);break;case otim::PT_GROUPCHAT_MEMBERS_GET:this->getGroupMember(clientContext, req, resp);break;default:MLOG_DEBUG("the type is invalid:"<<otim::etos((otim::PACK_TYPE)req.header.type));return otim::EC_PROTOCOL;}
群聊相关请求实现方法:
int syncGroup(const otim::ClientContext & clientContext, const otim::OTIMPack & req, otim::OTIMPack & resp);int createGroup(const otim::ClientContext & clientContext, const otim::OTIMPack & req, otim::OTIMPack & resp);int dismissGroup(const otim::ClientContext & clientContext, const otim::OTIMPack & req, otim::OTIMPack & resp);int updateGroupCreator(const otim::ClientContext & clientContext, const otim::OTIMPack & req, otim::OTIMPack & resp);int updateGroupInfo(const otim::ClientContext & clientContext, const otim::OTIMPack & req, otim::OTIMPack & resp);int joinGroup(const otim::ClientContext & clientContext, const otim::OTIMPack & req, otim::OTIMPack & resp);int quitGroup(const otim::ClientContext & clientContext, const otim::OTIMPack & req, otim::OTIMPack & resp);int getGroupMember(const otim::ClientContext & clientContext, const otim::OTIMPack & req, otim::OTIMPack & resp);
专用RPC接口
主要提供两个接口:
interface GroupChatRPCServant
{int getGroupMember(string groupId, out vector<string> memberIds);bool isGroupMember(string groupId, string memberId);};
历史消息服务 HistoryMsgServer
该服务主要处理用户历史消息即相关的业务:
- 热会话同步
- 历史消息存取
- 高优先级消息存取
该服务提供通用RPC服务,主要服务对象为接入服务BrokerServer;
用户所有消息都通过该服务进行存取;为高效存取,历史消息主要存储在redis,存储量及时长可以根据需求进一步来做配置开发。
业务逻辑处理接口
该服务采用通用接口来处理客户端请求;
tars::Int32 processHotsessionReq(const otim::ClientContext & clientContext,const otim::OTIMPack & req,otim::OTIMPack &resp);tars::Int32 processPullHistoryReq(const otim::ClientContext & clientContext,const otim::OTIMPack & reqPack,otim::OTIMPack &respPack);tars::Int32 processHighPriorMsgSyncReq(const otim::ClientContext & clientContext,const otim::OTIMPack & reqPack,otim::OTIMPack &respPack);
冷存储服务器 OlapServer
该服务主要将IM数据存储到mysql中永久保存;专用RPC服务;
业务逻辑处理接口
interface OlapServant
{int saveMsg(otim::ClientContext clientContext, OTIMPack pack, string sessionId, long seqId);
};
消息操作服务 MsgOperatorServer
该服务主要有如下功能逻辑处理:
- 消息控制请求(包含撤回,删除,覆写)
- 消息已读处理
业务逻辑处理接口
tars::Int32 processMsgUnreadReq(const otim::ClientContext & clientContext,const otim::OTIMPack & reqPack,otim::OTIMPack &respPack);tars::Int32 processMsgCTRLReq(const otim::ClientContext & clientContext,const otim::OTIMPack & reqPack,otim::OTIMPack &respPack);
消息撤回,删除,覆写逻辑处理
tars::Int32 MsgOperatorServantImp::processMsgCTRLReq(const otim::ClientContext & clientContext,const otim::OTIMPack & reqPack,otim::OTIMPack &respPack)
{SCOPELOGGER(scopelogger);scopelogger<<"reqPack:"<<reqPack.header.writeToJsonString();otim::MsgControl req;otim::unpackTars<otim::MsgControl>(reqPack.payload, req);MLOG_DEBUG("clientContext:"<<clientContext.writeToJsonString()<<" req:"<<req.writeToJsonString());respPack = reqPack;respPack.header.flags |= otim::PF_ISACK;otim::CommonErrorCode respData;respData.code = otim::EC_SUCCESS;if (req.sessionId.empty() || req.seqId == 0 || req.packId.empty()){respData.code = otim::EC_PARAM;otim::packTars<otim::CommonErrorCode>(respData, respPack.payload);MLOG_DEBUG("sessionId,packId or seqId is is empty req:"<<req.writeToJsonString());return respData.code;}otim::RedisConnPtr redis(otim::RedisPool::instance());//get old msgstd::vector<std::string> msgs;std::vector<std::string> scores;EMRStatus ret = redis->ZRangeByScoreAndLimit(otim::RKEY_MSG + req.sessionId, req.seqId, 5, msgs);if (EMRStatus::EM_KVDB_ERROR == ret){MLOG_ERROR("get msg fail!, sessionId:" << req.sessionId<<" msgId:"<<req.seqId);respData.code = otim::EC_DB_ERROR;otim::packTars<otim::CommonErrorCode>(respData, respPack.payload);return otim::EC_DB_ERROR;}MLOG_DEBUG("get old msg size:"<<msgs.size());otim::OTIMPack packOrg;for (auto item : msgs){std::vector<char> vctItem(item.begin(), item.end());otim::OTIMPack packItem;otim::unpackTars<otim::OTIMPack>(vctItem, packItem);MLOG_DEBUG("msgs :"<<packItem.header.writeToJsonString());if (packItem.header.packId == req.packId){packOrg = packItem;}}if (packOrg.header.packId.empty()){MLOG_WARN("The org msg is not exist:"<<req.sessionId<<" packId:"<<req.packId <<" seqId:"<<req.seqId);respData.code = otim::EC_MSG_NOT_EXIST;otim::packTars<otim::CommonErrorCode>(respData, respPack.payload);return respData.code;}MLOG_DEBUG("org msg:"<<packOrg.header.writeToJsonString());std::string to;if (req.command == otim::MC_REVOKE){packOrg.header.flags |= otim::PF_REVOKE;MLOG_DEBUG("revoke msg:"<<req.packId);}else if (req.command == otim::MC_OVERRIDE){packOrg.header.flags |= otim::PF_OVERRIDE;otim::MsgReq msgReq;otim::unpackTars<otim::MsgReq>(packOrg.payload, msgReq);msgReq.content = req.content;otim::packTars<otim::MsgReq>(msgReq, packOrg.payload);to = msgReq.to;MLOG_DEBUG("override msg:"<<req.packId);}else if (req.command == otim::MC_DELETE){
// packOrg.header.flags |= otim::PF_REVOKE;MLOG_DEBUG("delete msg:"<<req.packId);}else{MLOG_WARN("The command is error:"<<req.command<<" packId:"<<req.packId);respData.code = otim::EC_MSG_OP_CMD;otim::packTars<otim::CommonErrorCode>(respData, respPack.payload);return otim::EC_MSG_OP_CMD;}ret = redis->ZSetRemoveByScore(otim::RKEY_MSG + req.sessionId, req.seqId, req.seqId);if (EMRStatus::EM_KVDB_SUCCESS != ret ){MLOG_ERROR("delete original msg fail:"<<(int)ret);}//增加新的消息if (req.command != otim::MC_DELETE){std::string msgSave;otim::packTars<otim::OTIMPack>(packOrg, msgSave);ret = redis->ZSetAdd(otim::RKEY_MSG + req.sessionId, req.seqId, msgSave);if ( EMRStatus::EM_KVDB_SUCCESS != ret ){MLOG_ERROR("save cancel msg fail!");}}//通知在线接收者其他端otim::sendPackToMySelf(clientContext, reqPack);// send to userstd::vector<std::string> vctUserId;if (packOrg.header.type == otim::PT_MSG_SINGLE_CHAT || packOrg.header.type == otim::PT_MSG_BIZ_NOTIFY){if (to.empty()){otim::MsgReq msgReq;otim::unpackTars<otim::MsgReq>(packOrg.payload, msgReq);to = msgReq.to;}vctUserId.push_back(to);MLOG_DEBUG("single or notify chat packId:"<<packOrg.header.packId<<" to:"<<to);}else if (packOrg.header.type == otim::PT_MSG_GROUP_CHAT){//get groupMemberotim::GroupChatRPCServantPrx groupChatRPCServantPrx = otim::getServantPrx<otim::GroupChatRPCServantPrx>(PRXSTR_GROUP_CHAT_RPC);groupChatRPCServantPrx->getGroupMember(req.sessionId, vctUserId);MLOG_DEBUG("group chat packId:"<<packOrg.header.packId<<" to:"<<req.sessionId<<" member Size:"<<vctUserId.size());}int64_t seqId = otim::genSeqId();for (auto userId : vctUserId){otim::savePriorityMsg(redis.get(), reqPack, userId, seqId);otim::dispatchMsg(clientContext, reqPack, userId);}respData.code = otim::EC_SUCCESS;otim::packTars<otim::CommonErrorCode>(respData, respPack.payload);return otim::EC_SUCCESS;
}
Http接口服务
该服务针对第三方提供消息能力,主要提供如下接口:
- 发送消息(简单文本消息,复杂消息)
- 添加好友
- 删除好友
- 查看好友
功能实现函数
std::string doSendSimpleMsgCmd(TC_HttpRequest &cRequest);std::string doSendMsgCmd(TC_HttpRequest & cRequest);std::string doAddFriend(TC_HttpRequest &request);std::string doDelFriend(TC_HttpRequest &request);std::string doGetFriends(TC_HttpRequest &request);
Push推送服务
该服务主要实现IM消息的离线推送能力, APP客户端不在线的场景下,将消息通过离线通道push用户的手机上,以提高消息的触达率。
主要实现iOS APNS,Android FCM,华为,小米,oppo,vivo等厂商的离线消息推送功能;
需要根据各个厂商开放平台提供的API进行开发集成。
Android 厂商开发平台地址:
- 华为:
https://developer.huawei.com/consumer/cn/hms/huawei-pushkit - 小米:
https://dev.mi.com/console/appservice/push.html - 魅族:
http://open-wiki.flyme.cn/doc-wiki/index - vivo:
https://push.vivo.com.cn/#/ - oppo:
https://push.oppo.com/
RPC接口
enum PushServiceType
{PS_TYPE_NONE = 0, //无 Push服务提供商PS_TYPE_IOS = 1, //IOS Push服务提供商PS_YPE_HUAWEI = 2, //华为 Push服务提供商PS_TYPE_XIAOMI = 3, //小米 Push服务提供商PS_TYPE_MEIZU = 4, //魅族 Push服务提供商PS_TYPE_VIVO = 5, //vivi服务PS_TYPE_OPPO = 6, //oppo服务PS_TYPE_FCM = 7, //FCM服务
};struct RegInfo {0 require string packId = ""; //消息的id1 require PushServiceType serviceType = 0; //push服务提供商2 require string packageName = ""; //包名3 require string userId = ""; //用户id4 optional string appVersion = ""; //app version
};struct PushInfo {0 require string packId = ""; //消息的id1 require string userId = ""; //用户id2 require int unReadCount = 0; //未读消息数3 require string title = ""; /push标题4 require string content = ""; //push内容5 optional string uri = ""; //跳转uri6 optional string extraData=""; //业务自定义字段
};interface PushServant
{int register(RegInfo regInfo);int pushMessage(PushInfo pushInfo);
};
服务端部署
编译打包
所有服务开发完成后,执行如下命令进行编译,打包:
make release
make clean all
make tar
程序包部署
根据前期部署好的Tars框架环境、web管理系统,将程序包逐个发布,发布后的系统如图:

相关文章:
基于Tars高并发IM系统的设计与实现-实战篇5
基于Tars高并发IM系统的设计与实现-实战篇5 群聊服务 GroupChatServer 群聊服务既可以接受来自BrokerServer的用户请求,也需要接收来自其他服务的RPC请求;所以本服务提供两套RPC接口:通用RPC接口和专用RPC接口。 通用RPC接口 通用RPC接口主要处理如下…...
水溶性Cyanine3 N3叠氮化物Cy3 azide星戈瑞
欢迎来到星戈瑞荧光stargraydye! ICG-DBCO点击化学反应在生物标记物探测中应用。通过将ICG-DBCO与具有炔基的生物标记物结合,可以实现快速、选择性和稳定的共价连接,从而实现生物标记和探测。 **以下是ICG-DBCO点击化学反应在生物标记物探测中的一些应用…...
客户案例 | 永续发展,低代码助力“双碳”战略历史使命
关键发现 客户痛点:应对企业数字化转型,新技术能否提升绩效的不确定性,投资带来的风险性,以及企业组织架构的适应性等难点问题。作为业务驱动型企业,欠缺快速构建数字化产品方案的能力。 解决方案:利用西门…...
[保研/考研机试] KY187 二进制数 北京邮电大学复试上机题 C++实现
描述 大家都知道,数据在计算机里中存储是以二进制的形式存储的。 有一天,小明学了C语言之后,他想知道一个类型为unsigned int 类型的数字,存储在计算机中的二进制串是什么样子的。 你能帮帮小明吗?并且,小…...
SpringBoot 热部署
一、启动热部署 1.1 开启开发者工具 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional> </dependency>…...
BLE蓝牙协议栈分析
BLE——协议层次结构 一、BLE Controller Controller实现射频相关的模拟和数字部分,完成最基本的数据发送和接收,Controller对外接口是天线,对内接口是主机控制器接口HCI(Hostcontroller interface); 控制…...
flutter开发实战-BackdropFilter高斯模糊子Widget控件
flutter开发实战-BackdropFilter高斯模糊子Widget。 最近开发过程中遇到需要将控件进行模糊,比如iOS的effect的模糊效果。那在flutter中就需要用到了BackdropFilter 一、BackdropFilter BackdropFilter属性定义 BackdropFilter({Key key, required ImageFilter …...
嵌入式面试刷题(day3)
文章目录 前言一、怎么判断两个float是否相同二、float数据可以移位吗三、数据接收和发送端大小端不一致怎么办四、怎么传输float类型数据1.使用联合进行传输2.使用字节流3.强制类型转换 总结 前言 本篇文章我们继续讲解嵌入式面试刷题,给大家继续分享嵌入式中的面…...
JVM源码剖析之Java命令行参数全解
最近,有一位网友询问关于Java命令行参数方面的问题,因为在Java中参数有很多种,有不少的读者一直没弄明白,所以特意写下此篇文章。 此篇文章分2大块,第一块是不同参数的解释,第2块就是JVM源码论证ÿ…...
抽象工厂模式-java实现
介绍 抽象工厂模式基于工厂方法模式引入了“产品族”的概念,即我们认为具体产品是固定的,具体产品存在等级之分,比如我们常说的手机,有“青春版”,“至尊版”,“至臻版”。一个产品有多个版本族。这时候&a…...
机器学习笔记 - 基于Python发现最佳计算机视觉模型的神经架构搜索技术NAS
一、简述 近年来,随着深度学习技术的兴起,计算机视觉领域取得了巨大进步。事实证明,卷积神经网络 (CNN) 在图像识别任务中异常强大,但针对特定问题设计最佳架构仍然是一项具有挑战性的任务。这就是神经架构搜索(NAS)发挥作用的地方。NAS 是一种尖端技术,可以自动发现高性…...
机器学习---自编码器
自编码器过程 输入一个图片,经过encoder变成一个向量,再通过decoder将这个向量反向生成输入的图片。 这里我们希望输入和输出越接近越好。这个过程我们称为重建。 特点:不需要任何的标注资料。 在2006年这个思想就被提出来了: …...
vuejs 设计与实现 - 渲染器的设计
渲染器与响应式系统的结合 本节,我们暂时将渲染器限定在 DOM 平台。既然渲染器用来渲染真实 DOM 元素,那么严格来说,下面的函数就是一个合格的渲染器: // 渲染器: function renderer(domString, container) {container.innerHTM…...
openCV 图像对象的创建和赋值
文章目录 一、赋值二、克隆三、拷贝四、初始化 一、赋值 赋值操作是将一个cv::Mat对象的数据复制到另一个对象中。赋值操作使用的是浅拷贝(shallow copy),即两个对象共享相同的数据内存。这意味着对一个对象的修改会影响到另一个对象 cv::M…...
idea - 刷新 Git 分支数据 / 命令刷新 Git 分支数据
一、idea - 刷新 Git 分支数据 idea 找到 fetch 选项,重新获取分支数据 二、命令刷新 Git 分支数据 git fetch参考链接 1. 远程Gitlab新建的分支在IDEA里不显示...
线上电影购票选座H5小程序源码开发
搭建一个线上电影购票选座H5小程序源码需要一些基本的技术和步骤。以下是一个大致的搭建过程,可以参考: 1. 确定需求和功能:首先要明确你想要的电影购票选座H5小程序的需求和功能,例如用户登录注册、电影列表展示、选座购票、订单…...
QT正则校验
文章目录 前言一、Qt正则校验1.对输入框进行校验,不允许输入其他字符2.直接校验字符串 二、常用正则校验表达式 前言 项目中会经常遇到需要对字符串进行校验的情况,需要用到正则表达式(Regular Expression,通常简写为RegExp、RE等…...
ChatGPT“侵入”校园,教学评价体制受冲击,需作出调整
北密歇根大学的教授奥曼在学生作业中发现了一篇关于世界宗教的“完美论文”。“这篇文章写得比大多数学生都要好......好到不符合我对学生的预期!”他去问ChatGPT:“这是你写的吗?”ChatGPT回答:“99.9%的概率是的。” ChatGPT“侵…...
函数的声明和定义
1、函数声明 //告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数声明决定不了。 //函数的声明一般出现在函数的使用之前。要满足先声明后使用。 //函数的声明一般要放在头文件中的。 2、函数的定义 //函数…...
06微服务间的通信方式
一句话导读 微服务设计的一个挑战就是服务间的通信问题,服务间通信理论上可以归结为进程间通信,进程可以是同一个机器上的,也可以是不同机器的。服务可以使用同步请求响应机制通信,也可以使用异步的基于消息中间件间的通信机制。同…...
Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
使用VSCode开发Django指南
使用VSCode开发Django指南 一、概述 Django 是一个高级 Python 框架,专为快速、安全和可扩展的 Web 开发而设计。Django 包含对 URL 路由、页面模板和数据处理的丰富支持。 本文将创建一个简单的 Django 应用,其中包含三个使用通用基本模板的页面。在此…...
(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...
CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...
CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云
目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...
毫米波雷达基础理论(3D+4D)
3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文: 一文入门汽车毫米波雷达基本原理 :https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...
LangChain 中的文档加载器(Loader)与文本切分器(Splitter)详解《二》
🧠 LangChain 中 TextSplitter 的使用详解:从基础到进阶(附代码) 一、前言 在处理大规模文本数据时,特别是在构建知识库或进行大模型训练与推理时,文本切分(Text Splitting) 是一个…...
Mac flutter环境搭建
一、下载flutter sdk 制作 Android 应用 | Flutter 中文文档 - Flutter 中文开发者网站 - Flutter 1、查看mac电脑处理器选择sdk 2、解压 unzip ~/Downloads/flutter_macos_arm64_3.32.2-stable.zip \ -d ~/development/ 3、添加环境变量 命令行打开配置环境变量文件 ope…...
内窥镜检查中基于提示的息肉分割|文献速递-深度学习医疗AI最新文献
Title 题目 Prompt-based polyp segmentation during endoscopy 内窥镜检查中基于提示的息肉分割 01 文献速递介绍 以下是对这段英文内容的中文翻译: ### 胃肠道癌症的发病率呈上升趋势,且有年轻化倾向(Bray等人,2018&#x…...
