基于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微服务间的通信方式
一句话导读 微服务设计的一个挑战就是服务间的通信问题,服务间通信理论上可以归结为进程间通信,进程可以是同一个机器上的,也可以是不同机器的。服务可以使用同步请求响应机制通信,也可以使用异步的基于消息中间件间的通信机制。同…...
CVPR 2025 MIMO: 支持视觉指代和像素grounding 的医学视觉语言模型
CVPR 2025 | MIMO:支持视觉指代和像素对齐的医学视觉语言模型 论文信息 标题:MIMO: A medical vision language model with visual referring multimodal input and pixel grounding multimodal output作者:Yanyuan Chen, Dexuan Xu, Yu Hu…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
《Playwright:微软的自动化测试工具详解》
Playwright 简介:声明内容来自网络,将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具,支持 Chrome、Firefox、Safari 等主流浏览器,提供多语言 API(Python、JavaScript、Java、.NET)。它的特点包括&a…...
【CSS position 属性】static、relative、fixed、absolute 、sticky详细介绍,多层嵌套定位示例
文章目录 ★ position 的五种类型及基本用法 ★ 一、position 属性概述 二、position 的五种类型详解(初学者版) 1. static(默认值) 2. relative(相对定位) 3. absolute(绝对定位) 4. fixed(固定定位) 5. sticky(粘性定位) 三、定位元素的层级关系(z-i…...
LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...
[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #:…...
基于Java+VUE+MariaDB实现(Web)仿小米商城
仿小米商城 环境安装 nodejs maven JDK11 运行 mvn clean install -DskipTestscd adminmvn spring-boot:runcd ../webmvn spring-boot:runcd ../xiaomi-store-admin-vuenpm installnpm run servecd ../xiaomi-store-vuenpm installnpm run serve 注意:运行前…...
