C++微服务实战中好友管理子服务的全面解析

C++微服务实战中好友管理子服务的全面解析

【C++ 微服务实战】IM 好友管理子服务全解析:从 Proto 定义到高可用部署

在即时通讯(IM)系统中,好友管理子服务是连接 “用户社交关系” 与 “聊天会话” 的核心枢纽 —— 它既要处理好友申请、关系维护,也要管理单聊 / 群聊会话的创建与成员维护。本文基于实际项目代码(C++/brpc/Protobuf/ODB),从 “接口设计”“数据模型”“核心逻辑”“高可用部署” 四个维度,完整拆解好友管理子服务的实现细节,带你理解如何构建一个解耦、可靠的微服务。

一、服务定位与技术栈

在 IM 微服务架构中,好友管理子服务(Friend Server)的核心职责是 **“管理用户社交关系” 与 “维护聊天会话容器”**,向上对接网关服务接收客户端请求,向下依赖 MySQL/ES 存储数据,横向调用用户服务、消息服务完成协作。

1. 核心业务范围

  • 好友关系:申请、同意 / 拒绝、删除、查询好友列表;
  • 会话管理:创建单聊 / 群聊会话、查询会话列表、获取群成员;
  • 用户搜索:基于 ES 实现昵称 / 手机号模糊搜索(过滤已加好友);
  • 申请事件:管理好友申请的生命周期(待处理→已处理)。

2. 技术栈选型

技术组件作用说明项目中的封装 / 使用场景
brpc高性能 RPC 框架搭建 RPC 服务器,提供 Protobuf 接口
Protobuf数据序列化 / 接口定义定义服务接口(如 FriendAdd/GetChatSessionList
ODBMySQL 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),服务端需校验:

  1. A 和 B 是否已为好友(查 relation 表);
  2. A 是否已向 B 发送过申请(查 friend_apply 表);
  3. 校验通过后生成 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 的申请时,服务端需:

  1. 校验 notify_event_id 对应的申请是否存在;
  2. 无论同意 / 拒绝,先删除申请事件(避免重复处理);
  3. 同意则创建 双向好友关系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→BB→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 调用其他服务获取,核心封装在 GetUserInfoGetRecentMsg 两个私有方法中:

(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 系统,也可复用在社交、电商等需要 “关系管理” 的业务场景中。

从代码实现来看,该服务的核心价值在于 **“业务与技术的平衡”**:既满足了好友申请、会话管理等业务需求,又通过解耦、高可用设计确保了服务的稳定性与可维护性,为后续系统扩容与功能迭代打下了坚实基础。

Read more

【MySQL数据库基础】(四)MySQL 表的操作通关指南:创建 / 修改 / 删除一网打尽

【MySQL数据库基础】(四)MySQL 表的操作通关指南:创建 / 修改 / 删除一网打尽

前言         上一篇我们讲了 MySQL 库的核心操作,作为 MySQL 数据存储的核心载体,数据表的操作更是开发和运维中的高频操作。从表的创建、结构设计,到日常的字段增删改、表名修改,再到最后的表删除,每一步都有对应的语法和实操细节,稍不注意就可能踩坑(比如误删字段导致数据丢失)。         这篇文章就基于 MySQL 实战场景,把表的全套操作讲透,从创建表的核心语法、存储引擎的差异,到修改表的各种场景,再到删除表的高危操作注意事项,让你一文掌握 MySQL 表操作的所有精髓,新手也能快速上手!下面就让我们正式开始吧! 一、创建表:打好基础,定好结构         创建数据表是表操作的第一步,也是最关键的一步 —— 表的结构设计直接决定了后续数据存储的效率和扩展性。MySQL 中创建表的语法支持自定义字段、字段类型、字符集、校验规则和存储引擎,灵活度拉满。 1. 核心创建语法         MySQL 创建表的官方标准语法如下,关键字和可选项的设计和库操作一脉相承,理解起来非常容易: CREATE TABLE

By Ne0inhk

MySQL 从入门到精通完全教程

目录 1. 前言 2. MySQL 基础认知 3. MySQL 安装与配置 4. MySQL 核心语法 5. 高级查询技巧 6. MySQL 函数 7. 数据约束 8. 事务管理 9. 索引优化 10. 存储过程与函数 11. 用户与权限管理 12. 性能优化实战 13. 常见问题与解决方案 1. 前言 1.1 什么是MySQL? MySQL 是一款开源的关系型数据库管理系统(RDBMS),基于SQL(结构化查询语言)实现数据管理,广泛应用于Web开发(如PHP+MySQL、Python+MySQL),特点是轻量、高效、跨平台、

By Ne0inhk
Flutter 组件 ansi_text 适配鸿蒙 HarmonyOS 实战:终端色彩渲染,构建高性能 ANSI 日志高亮与命令行交互架构

Flutter 组件 ansi_text 适配鸿蒙 HarmonyOS 实战:终端色彩渲染,构建高性能 ANSI 日志高亮与命令行交互架构

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 ansi_text 适配鸿蒙 HarmonyOS 实战:终端色彩渲染,构建高性能 ANSI 日志高亮与命令行交互架构 前言 在鸿蒙(OpenHarmony)生态迈向工业级运维、涉及大量后台守护进程(Daemon)、系统日志审计及开发者工具链(CLI)开发的背景下,如何为枯燥的纯文本终端注入具备视觉层级的色彩与样式,已成为提升调试效率与故障定位速度的“视觉助推器”。在鸿蒙设备这类强调 AOT 极致性能与低级别 shell 交互的环境下,如果应用依然依赖基础的单色字符串输出日志,由于由于信息流极其庞大且缺乏重点,极易由于由于“视觉疲劳”导致关键系统警告或业务异常被淹没在海量数据中。 我们需要一种能够支持 ANSI 转义序列、具备富文本样式(加粗/背景色)且兼容多种终端模拟器的文本渲染方案。 ansi_text 为 Flutter 开发者引入了基于标准

By Ne0inhk
Spring Web MVC从入门到实战

Spring Web MVC从入门到实战

—JavaEE专栏— 1. Spring Web MVC核心概念 1.1 什么是Spring Web MVC Spring Web MVC是基于Servlet API构建的原始Web框架,从一开始就包含在Spring框架中,其正式名称来源于源模块名称(spring-webmvc),通常简称为Spring MVC。 官方定义:Spring Web MVC is the original web framework built on the Servlet API and has been included in the Spring Framework from the very beginning. Servlet是Java Web开发的规范,定义了动态页面开发的技术标准,而Tomcat、Weblogic等Servlet容器则是该规范的具体实现,

By Ne0inhk