微服务即时通信系统---(九)消息转发子服务

embedded/2025/3/24 9:23:20/

目录

功能设计

模块划分

业务接口/功能示意图

服务实现流程

服务代码实现

数据管理

MySQL(聊天会话成员管理)

chatSessionMember.hxx(ODB文件编写)

客户端操作编写(mysqlChatSessionMemberTable.hpp)

编写proto文件

消息元信息

消息转发proto

发送新消息

RPC调用

服务端创建子类(MessageTransmiteServiceImpl)完成RPC服务调用函数重写

GetTransmiteTargetID(获取转发目标ID并存储消息)

服务端完成消息转发子服务类(MessageTransmiteServer)

注意

实例化服务类对象,启动服务

工程系统构建配置文件(CMakeLists.txt)

服务测试

MySQL的会话成员表测试

向User表新增用户信息

新消息发送测试


本章节,主要对项目中消息转发子服务模块进行分析、开发与测试。

功能设计

消息转发子服务,主要用于对于一条消息内容,组织消息的消息ID以及各项所需要素,然后通知入口网关子服务,消息应该发送给谁。

而消息都是以聊天会话为基础的,因此根据聊天会话找到它的所有成员,这就是转发的目标。

除此之外,消息转发子服务还需要将受到的消息,放入消息队列中,由消息存储子服务进行消费存储。

因此,消息转发子服务提供一个功能性接口:

1、获取消息转发目标:针对消息内容,组织消息,告知入口网关子服务转发目标。

模块划分

参数/配置文件解析模块基于gflags框架直接使用,进行参数/配置文件的解析。
日志模块基于spdlog封装的logger 直接进行日志输出。
服务注册模块基于etcd框架封装的注册模块 直接进行消息转发子服务模块的服务注册。
RPC服务模块基于brpc框架 搭建消息转发子服务的RPC服务器
服务发现与调用模块

基于etcd框架封装的服务发现与brpc框架封装的服务调用模块。

1、从用户管理子服务获取消息发送者的用户信息。

数据库数据操作模块

基于odb-mysql数据管理封装的模块,实现关系型数据库中数据的操作。

1、根据消息内容(会话ID),从数据库获取会话成员。

MQ发布模块基于rabbitmq-client封装的模块将消息发布到消息队列,让消息存储子服务进行消费,对消息进行存储。

业务接口/功能示意图

服务实现流程

1、编写服务所需的proto文件,利用protoc工具生成RPC服务器所需的.pb.h 和 .pb.cc 项目文件。
2、服务端 创建子类,继承于proto文件中RPC调用类,并进行功能性接口函数重写。
3、服务端 完成消息转发子服务类。
4、实例化 服务类对象,启动服务

服务代码实现

数据管理

MySQL(聊天会话成员管理)

因为本服务需要通过聊天会话ID,获取到会话内成员ID,因此需要对聊天会话成员表进行管理。

chatSessionMember.hxx(ODB文件编写)
#pragma once
#include <iostream>
#include <odb/nullable.hxx>
#include <odb/core.hxx>// odb -d mysql --std c++11 --generate-query --generate-schema --profile boost/date-time chatSessionMember.hxxnamespace yangz
{
#pragma db object table("ChatSessionMember")class ChatSessionMember{public:ChatSessionMember() {}ChatSessionMember(const std::string &cssid, const std::string &user_id): _chat_session_id(cssid), _user_id(user_id){}~ChatSessionMember() {}public:void set_chat_session_id(const std::string &cssid) { _chat_session_id = cssid; }std::string get_chat_session_id() { return _chat_session_id; }void set_user_id(const std::string &user_id) { _user_id = user_id; }std::string get_user_id() { return _user_id; }private:friend class odb::access;
#pragma db id autounsigned long _id; // 自增主键
#pragma db type("varchar(64)") index std::string _chat_session_id; // 聊天会话id, varchar(64), 被索引
#pragma db type("varchar(64)")std::string _user_id; // 会话内的用户ID};
}

编译生成sql文件指令:

odb -d mysql --std c++11 --generate-query --generate-schema --profile boost/date-time chatSessionMember.hxx

 此时在 .sql文件里新增:

然后将该.sql 文件导入数据库中:

mysql -uroot -p 'MicroChat' < chatSessionMember.sql 
Enter password: 

现在数据库中就有对应的表了:

客户端操作编写(mysqlChatSessionMemberTable.hpp)

该模块主要提供五个接口:

1、新增一个ChatSessionMember信息。

2、新增vector个ChatSessionMember信息。

3、删除指定ChatSessionMember信息。

4、删除所有信息。

5、通过聊天会话ID,获取会话内所有成员ID。

#pragma once
#include "odbMysqlHandleFactory.hpp"
#include "chatSessionMember.hxx"
#include "chatSessionMember-odb.hxx"
#include "logger.hpp"namespace yangz
{class ChatSessionMemberTableClient{public:using ptr = std::shared_ptr<ChatSessionMemberTableClient>;ChatSessionMemberTableClient(const std::shared_ptr<odb::core::database> &mysql_client) : _mysql_client(mysql_client) {}public:// 新增ChatSessionMemberbool append(ChatSessionMember &csm){try{odb::transaction trans(_mysql_client->begin());_mysql_client->persist(csm);trans.commit();}catch (const std::exception &e){LOG_ERROR("新增一个会话成员失败, cssid: {}, user_id: {}, 失败原因: {}", csm.get_chat_session_id(), csm.get_user_id(), e.what());return false;}}// 新增vector<ChatSessionMember>bool append(std::vector<ChatSessionMember> &csm_list){try{odb::transaction trans(_mysql_client->begin());for (auto &csm : csm_list){_mysql_client->persist(csm);}trans.commit();}catch (const std::exception &e){LOG_ERROR("新增多个会话成员失败, cssid: {}, user_size: {}, 失败原因: {}", csm_list[0].get_chat_session_id(), csm_list.size(), e.what());return false;}}// 删除指定的ChatSessionMemberbool remove(ChatSessionMember &csm){try{odb::transaction trans(_mysql_client->begin());typedef odb::query<ChatSessionMember> query;typedef odb::result<ChatSessionMember> result;_mysql_client->erase_query<ChatSessionMember>(query::chat_session_id == csm.get_chat_session_id() && query::user_id == csm.get_user_id());trans.commit();}catch (const std::exception &e){LOG_ERROR("删除指定会话成员失败, cssid: {}, user_id: {}, 失败原因: {}", csm.get_chat_session_id(), csm.get_user_id(), e.what());return false;}}// 删除所有会话成员bool remove(const std::string &cssid){try{odb::transaction trans(_mysql_client->begin());typedef odb::query<ChatSessionMember> query;typedef odb::result<ChatSessionMember> result;_mysql_client->erase_query<ChatSessionMember>(query::chat_session_id == cssid);trans.commit();}catch (const std::exception &e){LOG_ERROR("删除所有会话成员失败, cssid: {}, 失败原因: {}", cssid, e.what());return false;}}// 通过cssid获取会话内成员IDstd::vector<std::string> get_user_id_list(const std::string &cssid){std::vector<std::string> user_id_list;try{odb::transaction trans(_mysql_client->begin());typedef odb::query<ChatSessionMember> query;typedef odb::result<ChatSessionMember> result;result r(_mysql_client->query<ChatSessionMember>(query::chat_session_id == cssid));for (result::iterator i(r.begin()); i != r.end(); ++i){user_id_list.push_back(i->get_user_id());}trans.commit();}catch (const std::exception &e){LOG_ERROR("获取会话内成员ID失败, cssid: {}, 失败原因: {}", cssid, e.what());}return user_id_list;}private:std::shared_ptr<odb::core::database> _mysql_client;};
}

编写proto文件

消息元信息

对于一条消息来说,需要包含:消息ID、消息所属聊天会话ID、消息产生时间、消息发送者信息、消息内容。

而其中:消息内容这里,可能是文件信息、图像信息、语音信息、文字信息。因此消息内容这里,又需要包含:消息类型、消息内容。

消息元信息(MessageInfo)成员(主要是用于好友管理子服务中获取最近的聊天信息所用,此处不用):

1、消息ID:标识消息唯一性。

2、消息所属聊天会话ID:通过该ID,查找聊天会话成员。

3、产生时间:以时间戳的方式记录。

4、消息发

文章来源:https://blog.csdn.net/2302_80873119/article/details/146338611
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ppmy.cn/embedded/174512.html

相关文章

Apache Paimon 在抖音集团多场景中的优化实践

资料来源&#xff1a;火山引擎-开发者社区 本文将基于抖音集团内部两大业务的典型实时数仓场景&#xff0c;介绍Paimon在抖音集团内部的生产实践。 作者&#xff1a;李明、苏兴、文杰 抖音集团大数据工程师 目前抖音集团内部主要使用 Lambda 架构进行实时数仓建设&#xff0c;其…

【3D模型】【游戏开发】【Blender】Blender模型分享-狮头木雕附导入方法

导入方法&#xff1a; [Blender] 如何导入包含纹理的 .blend 模型文件 在 3D 建模和渲染工作中&#xff0c;Blender 是一款功能强大的免费开源软件。很多时候&#xff0c;我们需要导入 .blend 后缀的模型文件&#xff0c;同时确保纹理&#xff08;textures&#xff09;文件夹…

【K8S】ImagePullBackOff状态问题排查。

ImagePullBackOff 是在使用 Kubernetes&#xff08;K8s&#xff09;时经常遇到的一种错误状态&#xff0c;下面为你详细介绍其含义、可能的原因及解决办法。 含义 当你在 K8s 集群中创建一个 Pod 时&#xff0c;Kubelet 会尝试从指定的镜像仓库拉取所需的容器镜像。如果拉取镜…

多路归并算法

多路归并算法 是一种将多个有序序列合并为一个有序序列的算法。它是 归并排序 的扩展&#xff0c;适用于需要合并多个有序序列的场景&#xff0c;例如外部排序&#xff08;External Sorting&#xff09;或数据库中的多路归并连接&#xff08;Multiway Merge Join&#xff09;。…

GitHub 超火的开源终端工具——Warp

Warp 作为近年来 GitHub 上备受瞩目的开源终端工具&#xff0c;以其智能化、高性能和协作能力重新定义了命令行操作体验。以下从多个维度深入解析其核心特性、技术架构、用户评价及生态影响力&#xff1a; 一、背景与核心团队 Warp 由前 GitHub CTO Jason Warner 和 Google 前…

全面解析 HTML 标签:简写与全称及其应用

一、引言 在 HTML 的世界里&#xff0c;标签是构建网页结构与内容的基石。每个 HTML 标签都有着特定的功能和用途&#xff0c;而了解其简写形式以及对应的英文全称和中文说明&#xff0c;对于网页开发者而言至关重要。这不仅有助于准确编写代码&#xff0c;更能让我们清晰理解…

【go】忽略range循环中使用指针的影响?

经典面试题&#xff1a; func main() {s : Store{m: make(map[string]*Customer),}s.storeCustomers4([]Customer{{ID: "1", Balance: 10},{ID: "2", Balance: -10},{ID: "3", Balance: 0},})print(s.m) }func (s *Store) storeCustomers(custo…

Unity-VR中使用手柄点击UI

拓展BaseInputModule 使用鼠标模拟VR设备操作 using UnityEngine.EventSystems; using UnityEngine; namespace Framework.VR {/// <summary>///按下鼠标左键&#xff0c;手柄Z轴方向获取UI对象&#xff0c;通知对象被点击/// </summary>public class VRInputMod…