明确 UserServer 在 IM 系统中的定位
在 IM 项目的微服务架构里,用户服务(UserServer)是整个系统的基石。所有业务模块——无论是好友关系、消息流转还是朋友圈动态,都强依赖用户认证和基础信息。从实战角度看,设计 UserServer 不仅要关注功能落地,更要考虑扩展性、可测试性以及性能瓶颈的规避。
核心职责与架构考量
UserServer 主要承担三个核心职责:
- 用户认证:涵盖注册(用户名/手机号)、登录(密码/验证码)及会话管理;
- 信息管理:支持头像、昵称、签名等字段的修改与查询;
- 基础支撑:为其他服务提供用户上下文,例如好友服务查询资料、消息服务校验发送者身份。
设计之初就必须考虑横向扩展能力,比如后续接入第三方登录,以及通过 Redis 缓存会话来避免频繁查库带来的性能损耗。
依赖注入:让服务更灵活
在早期的 UserServiceImpl 实现中,我直接在类内部 new 了 DMSClient(真实短信服务)。后来发现个人开发者难以申请企业短信资质,想换成模拟服务时,不得不重构大量代码。重构后,我将所有外部依赖通过构造函数注入,系统变得清爽许多。
构造函数承担了依赖初始化的职责:
UserServiceImpl(
const MockSmsClient::ptr& mock_sms_client, // 短信服务(真实/模拟可替换)
const std::shared_ptr<elasticlient::Client>& es_client, // ES(用户搜索用)
const std::shared_ptr<odb::core::database>& mysql_client, // MySQL(用户数据存储)
const std::shared_ptr<sw::redis::Redis>& redis_client, // Redis(会话/验证码)
const ServiceManager::ptr& channel_manager, // 服务管理(调用文件服务等)
const std::string& file_service_name // 文件服务名称(定位服务用)
) : _es_user(std::make_shared<ESUser>(es_client)),
_mysql_user(std::make_shared<UserTable>(mysql_client)),
_redis_session(std::make_shared<Session>(redis_client)),
_redis_status(std::make_shared<Status>(redis_client)),
_redis_codes(std::make_shared<Codes>(redis_client)),
_file_service_name(file_service_name),
_mm_channels(channel_manager),
_dms_client(mock_sms_client) // 注入短信服务,而非内部 new {
_es_user->createIndex(); // 初始化 ES 用户索引
}
这种设计的好处显而易见:替换依赖无需触碰业务代码。将 MockSmsClient 换成真实 DMSClient,只需修改构建器(UserServerBuilder)的初始化逻辑, 方法完全不用动。同时,单元测试也能注入'假的'客户端,摆脱对真实中间件的依赖。


