C++微服务实战中好友管理子服务的全面解析
【C++ 微服务实战】IM 好友管理子服务全解析:从 Proto 定义到高可用部署
在即时通讯(IM)系统中,好友管理子服务是连接 “用户社交关系” 与 “聊天会话” 的核心枢纽 —— 它既要处理好友申请、关系维护,也要管理单聊 / 群聊会话的创建与成员维护。本文基于实际项目代码(C++/brpc/Protobuf/ODB),从 “接口设计”“数据模型”“核心逻辑”“高可用部署” 四个维度,完整拆解好友管理子服务的实现细节,带你理解如何构建一个解耦、可靠的微服务。
一、服务定位与技术栈
在 IM 微服务架构中,好友管理子服务(Friend Server)的核心职责是 **“管理用户社交关系” 与 “维护聊天会话容器”**,向上对接网关服务接收客户端请求,向下依赖 MySQL/ES 存储数据,横向调用用户服务、消息服务完成协作。
1. 核心业务范围
- 好友关系:申请、同意 / 拒绝、删除、查询好友列表;
- 会话管理:创建单聊 / 群聊会话、查询会话列表、获取群成员;
- 用户搜索:基于 ES 实现昵称 / 手机号模糊搜索(过滤已加好友);
- 申请事件:管理好友申请的生命周期(待处理→已处理)。
2. 技术栈选型
| 技术组件 | 作用说明 | 项目中的封装 / 使用场景 |
|---|---|---|
| brpc | 高性能 RPC 框架 | 搭建 RPC 服务器,提供 Protobuf 接口 |
| Protobuf | 数据序列化 / 接口定义 | 定义服务接口(如 FriendAdd/GetChatSessionList) |
| ODB | MySQL ORM 框架 | 映射数据库表到 C++ 对象(如 RelationTable) |
| MySQL | 关系型数据库 | 存储好友关系、会话信息、申请事件 |
| Elasticsearch | 全文搜索引擎 | 实现用户模糊搜索,过滤已加好友和当前用户 |
| Etcd | 服务注册与发现 | 注册自身服务,发现用户服务、消息服务地址 |
| gflags | 命令行参数解析 | 配置服务端口、数据库地址、日志等级等 |
| spdlog | 日志框架 | 输出调试 / 错误日志,支持文件 / 控制台双输出 |
二、ProtoBuf 接口设计:定义服务的 “通信契约”
微服务的核心是 “接口化协作”,好友管理子服务的对外交互全靠 Protobuf 定义的接口。以下结合项目代码,拆解核心接口的设计逻辑(对应 friend.pb.h)。
1. 核心接口概览
好友服务共提供 10 个核心接口,覆盖 “好友关系”“会话管理”“搜索” 三大场景,接口设计遵循 “单一职责” 原则:
| 接口名称 | 业务场景 | 核心作用 |
|---|---|---|
GetFriendList | 查看好友列表 | 返回当前用户的所有好友信息(昵称、头像等) |
FriendAdd | 发送好友申请 | 校验关系后创建申请事件,返回事件 ID |
FriendAddProcess | 处理好友申请 | 同意则创建好友关系 + 单聊会话,拒绝则删除事件 |
FriendRemove | 删除好友 | 移除好友关系 + 单聊会话 + 会话成员 |
FriendSearch | 搜索用户 | ES 模糊搜索,过滤已加好友和当前用户 |
GetPendingFriendEventList | 查看待处理申请 | 返回当前用户收到的所有未处理好友申请 |
GetChatSessionList | 查看聊天会话列表 | 返回单聊 / 群聊会话,包含最新消息 |
ChatSessionCreate | 创建群聊会话 | 生成群聊 ID,添加会话信息和成员 |
GetChatSessionMember | 查看群成员 | 返回指定群聊的所有成员信息 |
2. 关键接口字段解析(以好友申请为例)
以 FriendAdd(发送好友申请)和 FriendAddProcess(处理申请)为例,理解接口如何承载业务逻辑:
(1)FriendAdd:发送好友申请
// 发送好友申请请求 message FriendAddReq { string request_id = 1; // 链路追踪ID(分布式调用唯一标识) string user_id = 2; // 申请人ID(当前登录用户) string respondent_id = 3; // 被申请人ID(目标用户) } // 发送好友申请响应 message FriendAddRsp { string request_id = 1; // 对应请求ID bool success = 2; // 申请是否成功 string errmsg = 3; // 错误信息(失败时填充) string notify_event_id = 4; // 申请事件ID(用于后续处理) } 业务逻辑:
用户 A 申请加 B 为好友时,网关会先鉴权并填充 user_id(A 的 ID),服务端需校验:
- A 和 B 是否已为好友(查
relation表); - A 是否已向 B 发送过申请(查
friend_apply表); - 校验通过后生成
notify_event_id(UUID),存入friend_apply表。
(2)FriendAddProcess:处理好友申请
// 处理好友申请请求 message FriendAddProcessReq { string request_id = 1; // 链路追踪ID string user_id = 2; // 被申请人ID(当前登录用户) string apply_user_id = 3; // 申请人ID(A的ID) bool agree = 4; // 是否同意(true=同意,false=拒绝) string notify_event_id = 5; // 申请事件ID(对应FriendAdd返回的ID) } // 处理好友申请响应 message FriendAddProcessRsp { string request_id = 1; // 对应请求ID bool success = 2; // 处理是否成功 string errmsg = 3; // 错误信息 string new_session_id = 4; // 同意时生成的单聊会话ID(网关推送用) } 业务逻辑:
用户 B 处理 A 的申请时,服务端需:
- 校验
notify_event_id对应的申请是否存在; - 无论同意 / 拒绝,先删除申请事件(避免重复处理);
- 同意则创建 双向好友关系(
relation表插两条记录)、单聊会话(chat_session表)、会话成员(chat_session_member表插 A 和 B 的记录)。
三、ODB 数据模型:映射 “业务对象” 与 “数据库表”
好友服务的核心数据(好友关系、会话、申请事件)存储在 MySQL 中,通过 ODB(Object-Relational Mapping) 框架将 C++ 对象与数据库表关联,避免直接写 SQL 语句,提升代码可维护性。
1. 核心表结构与 ODB 映射
以下是好友服务依赖的 4 张核心表,对应 ODB 映射类与业务含义:
| 数据库表名 | ODB 映射类 | 核心字段(C++/MySQL) | 业务作用 |
|---|---|---|---|
relation(好友关系表) | RelationTable | _user_id(varchar64)/_peer_id(varchar64) | 存储双向好友关系(A→B 和 B→A) |
friend_apply(申请事件表) | FriendApplyTable | _event_id(varchar64)/_user_id/_peer_id | 存储好友申请的生命周期(待处理 / 已处理) |
chat_session(会话表) | ChatSessionTable | _chat_session_id(varchar64)/_session_type(tinyint) | 存储单聊 / 群聊会话元信息(类型、名称) |
chat_session_member(会话成员表) | ChatSessionMemeberTable | _chat_session_id/_user_id | 存储 “会话 - 用户” 关联(群聊成员、单聊双方) |
2. ODB 映射类实现(以好友关系表为例)
以 RelationTable 为例,展示 ODB 如何将 C++ 类映射到 MySQL 表:
// mysql_relation.hpp(ODB 映射类) #pragma once #include <odb/core.hxx> #include <string> namespace zrt { // 好友关系实体类(对应 relation 表) #pragma db object table("relation") class Relation { public: Relation() {} Relation(const std::string& user_id, const std::string& peer_id) : _user_id(user_id), _peer_id(peer_id) {} // Getter/Setter(省略) std::string user_id() const { return _user_id; } void user_id(const std::string& v) { _user_id = v; } std::string peer_id() const { return _peer_id; } void peer_id(const std::string& v) { _peer_id = v; } private: friend class odb::access; #pragma db id auto // 自增主键(MySQL 中为 bigint) unsigned long _id; // 数据库内部唯一标识 #pragma db type("varchar(64)") index // 加索引,加速按 user_id 查询 std::string _user_id; // 主动方用户ID(如 A 添加 B,则为 A) #pragma db type("varchar(64)") std::string _peer_id; // 被动方用户ID(如 A 添加 B,则为 B) }; // ODB 表操作类(封装 CRUD) class RelationTable { public: using ptr = std::shared_ptr<RelationTable>; RelationTable(const std::shared_ptr<odb::core::database>& db) : _db(db) {} // 插入双向好友关系(A→B 和 B→A) bool insert(const std::string& user_id, const std::string& peer_id) { try { odb::transaction t(_db->begin()); // 插入两条记录,确保双向好友关系 _db->persist(Relation(user_id, peer_id)); _db->persist(Relation(peer_id, user_id)); t.commit(); return true; } catch (const odb::exception& e) { LOG_ERROR("Insert relation failed: {}", e.what()); return false; } } // 检查好友关系是否存在 bool exists(const std::string& user_id, const std::string& peer_id) { try { odb::transaction t(_db->begin()); // 查 user_id→peer_id 是否存在 auto count = _db->query_value<Relation>( odb::query<Relation>::_user_id == user_id && odb::query<Relation>::_peer_id == peer_id ).count(); t.commit(); return count > 0; } catch (const odb::exception& e) { LOG_ERROR("Check relation exists failed: {}", e.what()); return false; } } // 其他方法:删除关系、查询用户的所有好友ID(省略) private: std::shared_ptr<odb::core::database> _db; }; } // namespace zrt 关键设计:
- 双向关系:插入好友时必须存两条记录(
A→B和B→A),确保双方查询好友列表时都能找到对方; - 事务保障:通过 ODB 的
transaction确保 “插入两条记录” 要么全成功,要么全失败,避免数据不一致; - 索引优化:
_user_id加索引,查询 “用户的所有好友” 时(select peer_id from relation where user_id=?)性能提升 10+ 倍。
四、核心服务实现:从接口逻辑到服务协作
好友服务的核心逻辑封装在 FriendServiceImpl 类中,通过 “接口实现 + 私有方法” 的方式,实现业务解耦与代码复用。以下拆解关键接口的实现细节,以及服务间的协作逻辑。
1. 服务初始化:依赖注入与解耦设计
FriendServiceImpl 的构造函数通过依赖注入(DI)传入所有外部依赖,避免硬编码,便于测试与扩展:
// FriendServiceImpl 构造函数(依赖注入) FriendServiceImpl( const std::shared_ptr<elasticlient::Client>& es_client, // ES 客户端 const std::shared_ptr<odb::core::database>& mysql_client, // MySQL 客户端 const ServiceManager::ptr& channel_manager, // RPC 信道管理器 const std::string& user_service_name, // 用户服务名称(Etcd 注册键) const std::string& message_service_name // 消息服务名称 ) : _es_user(std::make_shared<ESUser>(es_client)), // ES 用户搜索封装 _mysql_apply(std::make_shared<FriendApplyTable>(mysql_client)), // 申请事件表操作 _mysql_chat_session(std::make_shared<ChatSessionTable>(mysql_client)), // 会话表操作 _mysql_chat_session_member(std::make_shared<ChatSessionMemeberTable>(mysql_client)), // 会话成员表操作 _mysql_relation(std::make_shared<RelationTable>(mysql_client)), // 好友关系表操作 _user_service_name(user_service_name), _message_service_name(message_service_name), _mm_channels(channel_manager) {} // 管理用户服务、消息服务的 RPC 信道 解耦设计:
- 数据层解耦:通过
RelationTable/FriendApplyTable封装 MySQL 操作,业务逻辑不直接依赖 ODB; - 服务间解耦:通过
ServiceManager管理其他服务的 RPC 信道,避免直接硬编码服务地址(如用户服务地址变化时,只需更新 Etcd,无需改代码)。
2. 关键接口实现:以 “处理好友申请” 为例
FriendAddProcess 是好友服务最核心的接口之一,涉及 “申请事件删除、好友关系创建、会话创建” 三个关键步骤,需确保数据一致性:
void FriendServiceImpl::FriendAddProcess( ::google::protobuf::RpcController* controller, const ::zrt::FriendAddProcessReq* request, ::zrt::FriendAddProcessRsp* response, ::google::protobuf::Closure* done) { brpc::ClosureGuard rpc_guard(done); // 自动释放 Closure,避免内存泄漏 // 1. 定义错误回调(统一响应格式) auto err_response = [this, response](const std::string& rid, const std::string& errmsg) { response->set_request_id(rid); response->set_success(false); response->set_errmsg(errmsg); }; // 2. 提取请求关键参数 std::string rid = request->request_id(); std::string eid = request->notify_event_id(); // 申请事件ID std::string uid = request->user_id(); // 被申请人(B) std::string pid = request->apply_user_id(); // 申请人(A) bool agree = request->agree(); // 是否同意 // 3. 校验申请事件是否存在 bool ret = _mysql_apply->exists(pid, uid); if (!ret) { LOG_ERROR("{}-未找到{}-{}的好友申请事件", rid, pid, uid); return err_response(rid, "申请事件不存在"); } // 4. 删除申请事件(无论同意/拒绝,事件都需清理) ret = _mysql_apply->remove(pid, uid); if (!ret) { LOG_ERROR("{}-删除申请事件{}-{}失败", rid, pid, uid); return err_response(rid, "处理申请失败"); } // 5. 同意:创建好友关系、单聊会话、会话成员 std::string session_id; if (agree) { // 5.1 插入双向好友关系 ret = _mysql_relation->insert(uid, pid); if (!ret) { LOG_ERROR("{}-创建好友关系{}-{}失败", rid, uid, pid); return err_response(rid, "添加好友失败"); } // 5.2 创建单聊会话(类型=SINGLE,名称为空,客户端后续补全) session_id = uuid(); // 生成全局唯一会话ID(如 "SESSION-6e6b2d9a") ChatSession session(session_id, "", ChatSessionType::SINGLE); ret = _mysql_chat_session->insert(session); if (!ret) { LOG_ERROR("{}-创建单聊会话{}失败", rid, session_id); return err_response(rid, "创建会话失败"); } // 5.3 添加会话成员(A 和 B) std::vector<ChatSessionMember> members = { ChatSessionMember(session_id, uid), ChatSessionMember(session_id, pid) }; ret = _mysql_chat_session_member->append(members); if (!ret) { LOG_ERROR("{}-添加会话成员{}-{}失败", rid, uid, pid); return err_response(rid, "添加会话成员失败"); } } // 6. 组织响应(同意时返回会话ID,网关用于推送会话创建通知) response->set_request_id(rid); response->set_success(true); response->set_new_session_id(session_id); LOG_INFO("{}-处理好友申请成功:{}→{},agree={}", rid, pid, uid, agree); } 关键逻辑亮点:
- 错误统一处理:通过
err_response回调函数,确保所有错误场景的响应格式一致; - 数据一致性:删除申请事件后再创建关系,避免 “申请事件残留”;若创建会话失败,直接返回错误,后续可通过重试机制补全;
- 会话 ID 生成:用 UUID 确保全局唯一,避免不同服务实例生成重复会话 ID。
3. 服务间协作:调用用户服务与消息服务
好友服务本身不存储用户信息(如昵称、头像)和消息数据,需通过 RPC 调用其他服务获取,核心封装在 GetUserInfo 和 GetRecentMsg 两个私有方法中:
(1)调用用户服务批量获取用户信息
// 批量获取用户信息(调用用户服务的 GetMultiUserInfo 接口) bool FriendServiceImpl::GetUserInfo( const std::string& rid, const std::unordered_set<std::string>& uid_list, // 待获取的用户ID列表 std::unordered_map<std::string, UserInfo>& user_list) { // 输出:用户ID→用户信息 // 1. 从 ServiceManager 获取用户服务的 RPC 信道 auto channel = _mm_channels->choose(_user_service_name); if (!channel) { LOG_ERROR("{}-获取用户服务信道失败", rid); return false; } // 2. 构造 RPC 请求 GetMultiUserInfoReq req; GetMultiUserInfoRsp rsp; req.set_request_id(rid); for (const auto& uid : uid_list) { req.add_users_id(uid); // 批量添加用户ID,减少 RPC 调用次数 } // 3. 发起 RPC 调用 brpc::Controller cntl; zrt::UserService_Stub stub(channel.get()); stub.GetMultiUserInfo(&cntl, &req, &rsp, nullptr); // 4. 处理调用结果 if (cntl.Failed()) { LOG_ERROR("{}-调用用户服务失败:{}", rid, cntl.ErrorText()); return false; } if (!rsp.success()) { LOG_ERROR("{}-批量获取用户信息失败:{}", rid, rsp.errmsg()); return false; } // 5. 整理结果(用户ID→UserInfo) for (const auto& item : rsp.users_info()) { user_list.emplace(item.first, item.second); } return true; } 优化点:
- 批量调用:一次性获取多个用户信息,避免循环调用用户服务(减少网络开销,提升性能);
- 信道管理:
ServiceManager自动管理用户服务的地址(从 Etcd 发现),服务扩容 / 缩容时无需重启好友服务。
(2)调用消息服务获取会话最新消息
// 获取会话最新1条消息(调用消息服务的 GetRecentMsg 接口) bool FriendServiceImpl::GetRecentMsg( const std::string& rid, const std::string& session_id, // 会话ID MessageInfo& msg) { // 输出:最新消息 auto channel = _mm_channels->choose(_message_service_name); if (!channel) { LOG_ERROR("{}-获取消息服务信道失败", rid); return false; } GetRecentMsgReq req; GetRecentMsgRsp rsp; req.set_request_id(rid); req.set_chat_session_id(session_id); req.set_msg_count(1); // 只获取最新1条消息 brpc::Controller cntl; zrt::MsgStorageService_Stub stub(channel.get()); stub.GetRecentMsg(&cntl, &req, &rsp, nullptr); if (cntl.Failed() || !rsp.success()) { LOG_WARN("{}-获取会话{}最新消息失败:{}", rid, session_id, cntl.Failed() ? cntl.ErrorText() : rsp.errmsg()); return false; } // 提取最新消息 if (rsp.msg_list_size() > 0) { msg.CopyFrom(rsp.msg_list(0)); return true; } return false; } 业务价值:
用户查询会话列表时,需显示 “每条会话的最新消息”(如 “张三:下午开会”),好友服务通过调用消息服务获取该数据,避免自身存储消息,符合微服务 “数据私有” 原则。
五、服务搭建与高可用部署
好友服务通过 FriendServerBuilder 模式封装初始化流程,支持 “配置解析→依赖构建→服务启动” 的一站式部署,同时通过连接池、服务发现确保高可用。
1. Builder 模式:简化服务初始化
FriendServerBuilder 封装了 ES、MySQL、Etcd、RPC 服务器的初始化逻辑,屏蔽复杂细节:
class FriendServerBuilder { public: // 1. 构建 ES 客户端(连接 ES 集群) void make_es_object(const std::vector<std::string> host_list) { _es_client = ESClientFactory::create(host_list); } // 2. 构建 MySQL 客户端(带连接池) void make_mysql_object( const std::string& user, const std::string& pswd, const std::string& host, const std::string& db, const std::string& cset, int port, int conn_pool_count) { _mysql_client = ODBFactory::create( user, pswd, host, db, cset, port, conn_pool_count); } // 3. 构建服务发现(Etcd)与 RPC 信道管理 void make_discovery_object( const std::string& reg_host, // Etcd 地址 const std::string& base_service, // 服务监控根目录 const std::string& user_service_name, // 用户服务名称 const std::string& message_service_name) { // 消息服务名称 _mm_channels = std::make_shared<ServiceManager>(); _mm_channels->declared(user_service_name); // 声明需管理的服务 _mm_channels->declared(message_service_name); // Etcd 回调:服务上线/下线时更新信道 auto on_service_online = std::bind(&ServiceManager::onServiceOnline, _mm_channels.get(), std::placeholders::_1, std::placeholders::_2); auto on_service_offline = std::bind(&ServiceManager::onServiceOffline, _mm_channels.get(), std::placeholders::_1, std::placeholders::_2); // 初始化 Etcd 服务发现 _service_discoverer = std::make_shared<Discovery>( reg_host, base_service, on_service_online, on_service_offline); } // 4. 构建 RPC 服务器(brpc) void make_rpc_server(uint16_t port, int32_t timeout, uint8_t num_threads) { _rpc_server = std::make_shared<brpc::Server>(); // 创建好友服务实现类 FriendServiceImpl* service = new FriendServiceImpl( _es_client, _mysql_client, _mm_channels, _user_service_name, _message_service_name); // 添加服务到 RPC 服务器 if (_rpc_server->AddService(service, brpc::ServiceOwnership::SERVER_OWNS_SERVICE) != 0) { LOG_ERROR("添加好友服务到 RPC 服务器失败"); abort(); } // 配置 RPC 服务器(连接超时、IO 线程数) brpc::ServerOptions options; options.idle_timeout_sec = timeout; // 空闲连接超时 options.num_threads = num_threads; // IO 线程数(根据 CPU 核心数配置) if (_rpc_server->Start(port, &options) != 0) { LOG_ERROR("RPC 服务器启动失败(端口:{})", port); abort(); } } // 5. 注册服务到 Etcd(供网关发现) void make_registry_object( const std::string& reg_host, // Etcd 地址 const std::string& service_name, // 服务名称(如 /service/friend_service/instance) const std::string& access_host) { // 服务访问地址(如 192.168.1.100:10006) _registry_client = std::make_shared<Registry>(reg_host); _registry_client->registry(service_name, access_host); } // 6. 构建最终的好友服务实例 FriendServer::ptr build() { // 校验所有依赖是否初始化完成 if (!_es_client || !_mysql_client || !_rpc_server) { LOG_ERROR("服务依赖未初始化完成"); abort(); } return std::make_shared<FriendServer>( _service_discoverer, _registry_client, _es_client, _mysql_client, _rpc_server); } private: // 依赖对象(省略) Registry::ptr _registry_client; std::shared_ptr<elasticlient::Client> _es_client; std::shared_ptr<odb::core::database> _mysql_client; ServiceManager::ptr _mm_channels; Discovery::ptr _service_discoverer; std::shared_ptr<brpc::Server> _rpc_server; }; 2. 部署入口:main 函数解析配置与启动
main 函数通过 gflags 解析命令行参数,调用 Builder 构建服务并启动:
int main(int argc, char* argv[]) { // 1. 解析命令行参数(gflags) google::ParseCommandLineFlags(&argc, &argv, true); // 2. 初始化日志(调试模式输出控制台,发布模式输出文件) zrt::init_logger(FLAGS_run_mode, FLAGS_log_file, FLAGS_log_level); // 3. 构建好友服务 zrt::FriendServerBuilder fsb; // 3.1 初始化 ES(地址从参数获取) fsb.make_es_object({FLAGS_es_host}); // 3.2 初始化 MySQL(连接池大小=FLAGS_mysql_pool_count) fsb.make_mysql_object( FLAGS_mysql_user, FLAGS_mysql_pswd, FLAGS_mysql_host, FLAGS_mysql_db, FLAGS_mysql_cset, FLAGS_mysql_port, FLAGS_mysql_pool_count); // 3.3 初始化服务发现(Etcd + 信道管理) fsb.make_discovery_object( FLAGS_registry_host, FLAGS_base_service, FLAGS_user_service, FLAGS_message_service); // 3.4 初始化 RPC 服务器(端口=FLAGS_listen_port) fsb.make_rpc_server( FLAGS_listen_port, FLAGS_rpc_timeout, FLAGS_rpc_threads); // 3.5 注册服务到 Etcd fsb.make_registry_object( FLAGS_registry_host, FLAGS_base_service + FLAGS_instance_name, FLAGS_access_host); // 4. 启动服务(阻塞直到收到退出信号) auto server = fsb.build(); server->start(); return 0; } 部署关键参数:
FLAGS_mysql_pool_count:MySQL 连接池大小(建议设为 CPU 核心数的 2~4 倍,避免连接过多);FLAGS_rpc_threads:brpc IO 线程数(通常设为 CPU 核心数,充分利用多核性能);FLAGS_access_host:服务对外访问地址(需与网关在同一网络,确保网关能调用)。
六、技术亮点与扩展方向
1. 核心技术亮点
(1)解耦设计:服务间无硬依赖
通过 ServiceManager 管理其他服务的 RPC 信道,服务地址变化时只需更新 Etcd,无需修改代码;数据层通过 ODB 封装 MySQL 操作,更换数据库时只需修改 ODB 映射,不影响业务逻辑。
(2)数据一致性保障
- ODB 事务:确保 “插入双向好友关系”“删除申请事件 + 创建会话” 等操作的原子性;
- 唯一 ID 生成:申请事件 ID、会话 ID 用 UUID 确保全局唯一,避免分布式环境下的 ID 冲突。
(3)性能优化
- 批量 RPC 调用:获取好友列表时批量调用用户服务,减少网络开销;
- ES 搜索过滤:搜索用户时先过滤已加好友和当前用户,避免无效结果;
- 索引优化:MySQL 表的
user_id/chat_session_id加索引,查询性能提升 10~100 倍。
(4)高可用设计
- 连接池:MySQL 连接池避免频繁创建 / 销毁连接;
- 服务发现:Etcd 实现服务动态发现,服务下线时自动切换到其他实例;
- 日志监控:spdlog 输出详细日志,便于问题排查。
2. 后续扩展方向
- 好友备注功能:在
relation表中添加remark字段,支持用户给好友设置备注名; - 黑名单功能:新增
blacklist表,支持屏蔽非好友消息; - 会话权限管理:群聊添加 “管理员”“禁言” 功能,在
chat_session_member表中添加role字段; - 数据分片:用户量增大时,按
user_id对 MySQL 表进行分片,避免单表数据量过大。
七、总结
好友管理子服务作为 IM 系统的核心组件,通过 “Proto 接口定义规范通信、ODB 映射隔离数据层、ServiceManager 解耦服务协作、Builder 模式简化部署”,实现了一个高可用、可扩展的微服务。其设计思路不仅适用于 IM 系统,也可复用在社交、电商等需要 “关系管理” 的业务场景中。
从代码实现来看,该服务的核心价值在于 **“业务与技术的平衡”**:既满足了好友申请、会话管理等业务需求,又通过解耦、高可用设计确保了服务的稳定性与可维护性,为后续系统扩容与功能迭代打下了坚实基础。