在微服务即时通讯系统中,消息转发子服务扮演着至关重要的角色。它负责接收来自客户端的消息,确定消息的接收者,并将消息路由到正确的目的地。这个服务的设计直接影响到整个系统的消息传递效率和可靠性。
消息转发子服务主要承担以下职责:
这种设计将消息路由逻辑与消息传输逻辑解耦,使得系统能够更灵活地处理不同类型的消息传递场景。
系统采用了以下技术栈,每个选择都有其特定的考量:
gflags参数解析框架:
spdlog日志框架:
etcd服务注册与发现:
MySQL数据库:
RabbitMQ消息队列:
brpc RPC框架:
数据库模块是整个服务的数据基础,主要负责会话成员关系的管理。我们使用ODB ORM框架来简化数据库操作。
chat_session_member表的映射定义如下:
cpp复制#pragma db object table("chat_session_member")
class ChatSessionMember {
public:
// 构造函数和访问方法
private:
#pragma db id auto
unsigned long _id; // 自增主键
#pragma db type("varchar(64)") index
std::string _session_id; // 会话ID
#pragma db type("varchar(64)")
std::string _user_id; // 用户ID
};
ChatSessionMemeberTable类封装了所有数据库操作:
cpp复制class ChatSessionMemeberTable {
public:
// 添加单个成员
bool append(ChatSessionMember &csm) {
odb::transaction trans(_db->begin());
_db->persist(csm);
trans.commit();
}
// 获取会话所有成员
std::vector<std::string> members(const std::string &ssid) {
odb::transaction trans(_db->begin());
auto r = _db->query<ChatSessionMember>(
odb::query<ChatSessionMember>::session_id == ssid);
// 遍历结果集...
}
// 删除指定成员
bool remove(ChatSessionMember &csm) {
_db->erase_query<ChatSessionMember>(
odb::query<ChatSessionMember>::session_id == csm.session_id() &&
odb::query<ChatSessionMember>::user_id == csm.user_id());
}
};
注意:所有数据库操作都放在事务中执行,确保数据一致性。在实际生产环境中,还需要考虑连接池大小和超时设置。
RPC服务是消息转发子服务的核心接口,基于brpc框架实现。
protobuf定义的接口如下:
protobuf复制service MsgTransmitService {
rpc GetTransmitTarget(NewMessageReq) returns (GetTransmitTargetRsp);
}
message NewMessageReq {
string request_id = 1;
string user_id = 2;
string chat_session_id = 3;
MessageContent message = 4;
}
message GetTransmitTargetRsp {
string request_id = 1;
bool success = 2;
string errmsg = 3;
MessageInfo message = 4;
repeated string target_id_list = 5;
}
服务实现类TransmiteServiceImpl的核心流程:
cpp复制void GetTransmitTarget(google::protobuf::RpcController* controller,
const NewMessageReq* request,
GetTransmitTargetRsp* response,
::google::protobuf::Closure* done) {
// 1. 获取请求参数
std::string uid = request->user_id();
std::string chat_ssid = request->chat_session_id();
// 2. 查询发送者信息(通过用户子服务)
auto channel = _mm_channels->choose(_user_service_name);
UserService_Stub stub(channel.get());
stub.GetUserInfo(&cntl, &req, &rsp, nullptr);
// 3. 构造完整消息
MessageInfo message;
message.set_message_id(uuid()); // 生成唯一ID
message.set_timestamp(time(nullptr));
message.mutable_sender()->CopyFrom(rsp.user_info());
// 4. 发布到消息队列
_mq_client->publish(_exchange_name,
message.SerializeAsString(),
_routing_key);
// 5. 获取会话成员
auto target_list = _mysql_session_member_table->members(chat_ssid);
// 6. 组织响应
response->mutable_message()->CopyFrom(message);
for (const auto& id : target_list) {
response->add_target_id_list(id);
}
}
提示:在实际实现中,每个步骤都应该有完善的错误处理和日志记录。特别是RPC调用和消息队列发布,需要考虑重试机制。
服务通过gflags配置各种参数:
cpp复制DEFINE_string(registry_host, "http://127.0.0.1:2379", "服务注册中心地址");
DEFINE_string(instance_name, "/transmite_service/instance", "当前实例名称");
DEFINE_int32(listen_port, 10004, "Rpc服务器监听端口");
DEFINE_string(mysql_host, "127.0.0.1", "Mysql服务器访问地址");
DEFINE_string(mq_host, "127.0.0.1:5672", "消息队列服务器访问地址");
启动流程封装在TransmiteServerBuilder中:
cpp复制void start() {
// 1. 初始化各模块
make_mysql_object(...);
make_discovery_object(...);
make_registry_object(...);
make_mq_object(...);
make_rpc_server(...);
// 2. 构建并启动服务
auto server = build();
server->start();
}
我们提供了完整的测试用例来验证数据库操作:
cpp复制void append_test(bite_im::ChatSessionMemeberTable &tb) {
bite_im::ChatSessionMember csm1("会话ID1", "用户ID1");
tb.append(csm1);
bite_im::ChatSessionMember csm2("会话ID1", "用户ID2");
tb.append(csm2);
}
void ss_members(bite_im::ChatSessionMemeberTable &tb) {
auto res = tb.members("会话ID1");
for (auto &id : res) {
std::cout << id << std::endl; // 输出: 用户ID1\n用户ID2
}
}
进行端到端测试时需要关注:
消息完整性验证:
目标用户准确性:
性能测试指标:
在实际部署时,有几个关键点需要特别注意:
数据库优化:
消息队列优化:
RPC优化:
服务降级策略:
重试机制:
监控告警:
消息丢失问题:
目标用户不全:
性能瓶颈:
当前设计已经能满足基本需求,但随着业务发展,还可以考虑以下优化方向:
分片策略:
流控机制:
多协议支持:
扩展性增强:
这个转发服务的设计充分体现了微服务架构的优势 - 通过清晰的职责划分和松耦合设计,使得每个服务可以独立开发、部署和扩展。在实际项目中,这种设计显著提高了系统的可维护性和可扩展性。