用户管理子服务是IM(即时通讯)系统中的核心组件之一,负责处理用户全生命周期的管理需求。作为在Android平台上运行的IM应用的后端服务,它需要处理高并发的用户请求,同时保证数据的一致性和安全性。
这个服务主要解决以下几个核心问题:
在技术选型上,我们采用了多存储引擎协同工作的架构:
这种架构既保证了ACID特性(通过MySQL),又能满足高性能需求(通过Redis),同时提供了良好的扩展性(通过微服务拆分)。
用户表(User)是服务的核心数据存储,其字段设计如下:
| 字段名 | 类型 | 说明 | 索引 |
|---|---|---|---|
| user_id | VARCHAR(36) | 用户唯一标识 | 主键 |
| nickname | VARCHAR(15) | 用户昵称 | 唯一索引 |
| description | VARCHAR(100) | 个性签名 | 无 |
| password | VARCHAR(32) | 加密后的密码 | 无 |
| phone | VARCHAR(11) | 手机号 | 唯一索引 |
| avatar_id | VARCHAR(36) | 头像文件ID | 唯一索引 |
注意:avatar_id实际上是一个外键,指向文件服务中的文件记录。这种设计避免了在用户表中直接存储二进制数据,既符合数据库范式,又提高了查询效率。
索引策略基于实际查询场景设计:
经验分享:在用户系统中,避免为description这类长文本字段加索引。这类字段既可能包含大量文本,又很少作为精确查询条件,更适合用ES来实现搜索功能。
虽然当前设计是单表结构,但在实际生产环境中,当用户量超过500万时需要考虑分库分表。我们的预备方案是:
服务构造函数体现了整体架构:
cpp复制UserServiceImpl(
const std::shared_ptr<elasticlient::Client>& es_client,
const std::shared_ptr<odb::core::database>& mysql_client,
const std::shared_ptr<sw::redis::Redis>& redis_client,
const std::string& file_service_name,
const pcz::ServiceMannager::ptr& channel_mannager,
const std::shared_ptr<pcz::DMSClient>& dms_client):
_es_client(std::make_shared<pcz::ESUser>(es_client)),
_mysql_client(std::make_shared<pcz::UserTable>(mysql_client)),
_redis_session(std::make_shared<pcz::Session>(redis_client)),
_redis_status(std::make_shared<pcz::Status>(redis_client)),
_redis_code(std::make_shared<pcz::Code>(redis_client)),
_file_service_name(file_service_name),
_channel_mannager(channel_mannager),
_dms_client(dms_client) {
_es_client->createIndex();
}
各组件职责:
注册流程是用户系统的第一个关键路径,我们实现了两种注册方式:
mermaid复制sequenceDiagram
participant Client
participant UserService
participant MySQL
participant ES
Client->>UserService: 提交昵称和密码
UserService->>UserService: 格式校验(正则)
UserService->>MySQL: 查重
alt 昵称已存在
MySQL-->>UserService: 返回错误
else 昵称可用
UserService->>MySQL: 插入用户记录
UserService->>ES: 同步用户数据
ES-->>UserService: 操作结果
UserService-->>Client: 返回成功
end
关键校验逻辑:
cpp复制bool is_valid_nickname(const std::string& nickname) {
// 长度3-15个字符
if(nickname.length() < 3 || nickname.length() > 15)
return false;
// 只允许字母、数字、下划线和连字符
return std::all_of(nickname.begin(), nickname.end(), [](char c){
return isalnum(c) || c == '_' || c == '-';
});
}
bool is_valid_password(const std::string& password) {
// 长度6-15个字符
if(password.length() < 6 || password.length() > 15)
return false;
// 只允许字母和数字
return std::all_of(password.begin(), password.end(), ::isalnum);
}
手机号注册相比昵称注册多了验证码环节:
避坑指南:在实际生产中,建议使用专业的短信服务而非固定验证码。固定验证码仅适合测试环境使用,上线前务必替换为正规短信通道。
登录流程需要考虑会话管理和安全防护:
cpp复制void UserLogin(...) {
// 1. 提取凭证
std::string nickname = request->nickname();
std::string password = request->password();
// 2. 查询用户
auto user = _mysql_client->query_by_nickname(nickname);
if (!user) return error("用户不存在");
// 3. 密码校验
if (nullable_to_string(user->password()) != password)
return error("密码错误");
// 4. 检查在线状态
if (_redis_status->exists(user->user_id()))
return error("用户已在线");
// 5. 创建会话
std::string session_token = pcz::uuid();
_redis_session->append(session_token, user->user_id());
_redis_status->append(user->user_id());
// 6. 返回响应
response->set_success(true);
response->set_login_session_id(session_token);
}
安全增强措施:
头像处理展示了微服务协作的典型模式:
cpp复制void SetUserAvatar(...) {
// 1. 验证用户
auto user = _mysql_client->query_by_userid(user_id);
if (!user) return error("用户不存在");
// 2. 调用文件服务
auto channel = _channel_mannager->choose(_file_service_name);
pcz::FileService_Stub file_stub(channel.get());
pcz::PutSingleFileReq file_request;
file_request.mutable_file_data()->set_file_content(avatar_data);
// 3. 处理文件上传
pcz::PutSingleFileRsp file_response;
brpc::Controller cntl;
file_stub.PutSingleFile(&cntl, &file_request, &file_response, nullptr);
// 4. 更新引用
user->avatar_id(file_response.file_info().file_id());
_mysql_client->update(user);
}
优化实践:
采用多级缓存提升性能:
缓存更新策略:
cpp复制void updateUserCache(const std::string& user_id) {
// 1. 查询最新数据
auto user = _mysql_client->query_by_userid(user_id);
// 2. 更新Redis缓存
_redis_client->hmset("user:" + user_id, {
{"nickname", user->nickname()},
{"avatar", user->avatar_id()}
});
// 3. 设置过期时间
_redis_client->expire("user:" + user_id, 300);
}
连接池配置:
查询优化:
sql复制/* 反例:全表扫描 */
SELECT * FROM user WHERE description LIKE '%关键字%';
/* 正例:使用ES搜索 */
SELECT user_id FROM user WHERE nickname = '精确值';
cpp复制void batchInsertUsers(const std::vector<UserPtr>& users) {
odb::transaction t(_mysql_client->begin());
for(auto& user : users) {
_mysql_client->insert(user);
}
t.commit();
}
brpc最佳实践:
对所有输入参数进行严格校验:
cpp复制bool validatePhone(const std::string& phone) {
// 简单校验:11位数字
if(phone.length() != 11) return false;
return std::all_of(phone.begin(), phone.end(), ::isdigit);
// 实际生产环境应:
// 1. 校验号段有效性
// 2. 检查黑名单
// 3. 频率限制
}
cpp复制std::string generateSessionToken() {
// 使用密码学安全随机数
unsigned char buffer[16];
RAND_bytes(buffer, sizeof(buffer));
// 转为UUID格式
return boost::uuids::to_string(
boost::uuids::basic_random_generator<boost::mt19937>(buffer));
}
关键操作记录审计日志:
cpp复制void logSecurityEvent(const std::string& event, const std::string& user_id) {
std::string log = fmt::format(
"[{}] [{}] {} {}",
getCurrentTime(),
user_id,
event,
getClientIP()
);
_audit_logger->info(log);
}
审计事件包括:
采用分级错误码体系:
示例:
protobuf复制message Error {
int32 code = 1; // 错误码
string msg = 2; // 错误信息
string detail = 3; // 调试详情
}
关键依赖的熔断策略:
核心监控项:
Prometheus指标示例:
cpp复制// 注册指标
prometheus::Counter login_counter(
"user_login_total",
"Total login attempts",
{{"result", "success"}});
// 在代码中记录
if(login_success) {
login_counter.Increment({{"result", "success"}});
} else {
login_counter.Increment({{"result", "failure"}});
}
核心测试用例:
使用JMeter进行压力测试:
模拟故障场景:
验证系统的:
Docker Compose示例:
yaml复制version: '3'
services:
user-service:
image: user-service:v1.2
ports:
- "8000:8000"
depends_on:
- mysql
- redis
- elasticsearch
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
redis:
image: redis:6
ports:
- "6379:6379"
生产环境建议配置:
发布流程:
回滚条件:
在实际开发中,我们积累了一些关键经验:
cpp复制std::string hashPassword(const std::string& password) {
// 使用bcrypt算法
return BCrypt::generateHash(password, 12);
}
bool verifyPassword(const std::string& password, const std::string& hash) {
return BCrypt::validatePassword(password, hash);
}
这个用户管理子服务经过多次迭代,目前每天处理超过100万次请求,平均延迟在50ms以内,验证了架构设计的合理性。后续计划加入OAuth2.0支持和多因素认证,进一步提升系统安全性。