StatusServer
整个项目代码目录如下:
—AsioIOServicePool.h/.cpp
—ChatGrpcClient.h/.cpp 在这个项目这是没有用的
—ConfigMgr.h/.cpp
—const.h
—MysqlDao.h/.cpp
—MysqlMgr.h/.cpp
—RedisMgr.h/.cpp
—Singleton.h
—StatusServiceImpl.h/.cpp
—StatusServer.cpp
—config.ini
—message.proto以及生成的pb.cc/.h和grpc.pb.cc/.h
核心服务与入口
-
StatusServer.cpp
- 程序入口 (main):负责初始化并启动服务器。
- 启动流程:读取配置、初始化数据库和 Redis 连接池、构建并启动 gRPC 服务器。
- 信号处理:使用boost::asio监听SIGINT和 SIGTERM信号,实现服务器的优雅退出(Graceful Shutdown)。
-
StatusServiceImpl.h / .cpp
-
gRPC 服务实现:继承自StatusService::Service,实现了定义在message.proto中的接口。
-
核心功能:
-
Login:处理用户登录请求,校验用户 Token(通过查询 Redis)。 -
GetChatServer:负载均衡逻辑。根据各 ChatServer 的连接数(从 Redis 获取),返回一台负载最小的 ChatServer 地址给客户端。
-
-
内部逻辑:维护一个 ChatServer 列表,并包含生成唯一 Token 的辅助函数。
-
GateServer 调用 StatusServer
RegPost("/user_login", [](std::shared_ptr connection) {
auto body_str = boost::beast::buffers_to_string(connection->_request.body().data());
std::cout << "receive body is " << body_str << std::endl;
connection->_response.set(http::field::content_type, "text/json");
Json::Value root;
Json::Reader reader;
Json::Value src_root;
bool parse_success = reader.parse(body_str, src_root);
if (!parse_success) {
std::cout << "Failed to parse JSON data!" << std::endl;
root["error"] = ErrorCodes::Error_Json;
std::string jsonstr = root.toStyledString();
beast::ostream(connection->_response.body()) << jsonstr;
return true;
}
//
auto email = src_root["email"].asString();
auto pwd = src_root["passwd"].asString();
UserInfo userInfo;
//
bool pwd_valid = MysqlMgr::GetInstance()->CheckPwd(email, pwd, userInfo);
if (!pwd_valid) {
std::cout << " user pwd not match" << std::endl;
root["error"] = ErrorCodes::PasswdInvalid;
std::string jsonstr = root.toStyledString();
beast::ostream(connection->_response.body()) << jsonstr;
return true;
}
//登录成功后,获取聊天服务器地址
auto reply = StatusGrpcClient::GetInstance()->GetChatServer(userInfo.uid);
if (reply.error()) {
//处理错误
std::cout << " grpc get chat server failed, error is " << reply.error() << std::endl;
root["error"] = ErrorCodes::RPCFailed;
std::string jsonstr = root.toStyledString();
beast::ostream(connection->_response.body()) << jsonstr;
return true;
}
std::cout << "succeed to load userinfo uid is " << userInfo.uid << std::endl;
//返回给客户端
root["error"] = 0;
root["email"] = email;
root["uid"] = userInfo.uid;
root["token"] = reply.token();
root["host"] = reply.host();
root["port"] = reply.port();
std::string jsonstr = root.toStyledString();
beast::ostream(connection->_response.body()) << jsonstr;
return true;
});
}
StatusServer 负载均衡策略
//gRPC(远程过程调用,调用远程函数就像调用本地函数一样,RPC底层会做好数据的序列化与传输,应用于分布式服务)
Status StatusServiceImpl::GetChatServer(ServerContext* context, const GetChatServerReq* request, GetChatServerRsp* reply)
{
std::string prefix("llfc status server has received : ");
const auto& server = getChatServer(); //核心逻辑在这里!
reply->set_host(server.host);
reply->set_port(server.port);
reply->set_error(ErrorCodes::Success);
// 生成新 token 并存 Redis
reply->set_token(generate_unique_string());
insertToken(request->uid(), reply->token());
return Status::OK;
}
//选择连接数最少的服务器
ChatServer StatusServiceImpl::getChatServer() {
std::lock_guard guard(_server_mtx);
auto minServer = _servers.begin()->second;
auto count_str = RedisMgr::GetInstance()->HGet(LOGIN_COUNT, minServer.name);// 从 Redis 读每台服务器的在线人数
if (count_str.empty()) {
//不存在则默认设置为最大
minServer.con_count = INT_MAX;
}
else {
minServer.con_count = std::stoi(count_str);
}
// 使用范围基于for循环
for (auto& server : _servers) {
if (server.second.name == minServer.name) {
continue;
}
//这个地方COUNT怎么没有++?RedisMgr的HGet里面也没写?
//在chatserver里的logicsystem的LoginHandler方法里count++
//存在redis
auto count_str = RedisMgr::GetInstance()->HGet(LOGIN_COUNT, server.second.name);
if (count_str.empty()) {
server.second.con_count = INT_MAX;
}
else {
server.second.con_count = std::stoi(count_str);
}
if (server.second.con_count < minServer.con_count) {
minServer = server.second;
}
}
return minServer;
}
Login
-
调用位置: 暂未发现业务调用。
-
分析:
GateServer和ChatServer项目中的StatusGrpcClient.cpp都实现了Login方法的客户端封装(调用了stub->Login)。但是在GateServer和ChatServer的业务逻辑(如LogicSystem.cpp)中,没有发现调用StatusGrpcClient::Login的代码。ChatServer处理用户登录 (LoginHandler) 时,目前的实现是直接从 Redis/DB 获取数据校验,而没有向 StatusServer 汇报登录状态(或者相关代码未在当前搜索范围内)。
以下是运行结果:











