UserServer 在 IM 系统中的定位
做 IM 项目时,用户服务(UserServer)是整个系统的基石 —— 所有业务(好友、消息、朋友圈)都依赖用户认证和基础信息。从实战角度聊聊如何设计、实现 UserServer,包括核心功能落地、依赖替换(比如用模拟短信服务替代真实平台),以及那些踩过的坑。
一、核心职责与架构考量
UserServer 主要承担三个核心职责:
- 用户认证:注册(用户名/手机号)、登录(用户名密码/手机验证码)、会话管理;
- 用户信息管理:头像、昵称、签名、手机号的修改与查询;
- 基础支撑:给其他服务提供用户信息(比如好友服务查好友资料、消息服务查发送者信息)。
设计时必须考虑可扩展性(如后续加第三方登录)、可测试性(不用真实短信也能测手机号登录)以及性能(登录会话用 Redis 缓存,避免查库)。
二、核心设计:拒绝硬编码
1. 依赖注入:让服务更灵活
最开始写 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 用户索引
}
这样设计的好处很明显:


