Chap16-DistributedServiceDesign

Chap16-Distributed Service Design

完成了前面的前端的ui,我们开启后端的分布式设计。简而言之就是启动多个服务器,有一个StatusServer服务器用于查找压力最小,连接数量少的服务器,然后将服务器的地址传给前端选择连接。

这多个服务器实际上代码是一样的,除了配置文件略有不同。

  1. 配置

我们先从最简单的配置文件看起,先看StatusSerer的配置文件

[GateWayServer]
port=9999
[VarifyServer]
host=127.0.0.1
port=50051
[StatusServer]
host=127.0.0.1
port=50052

[ChatServers]
name=ChatServer1,ChatServer2
[ChatServer1]
name=ChatServer1
host=127.0.0.1
port=50055
RPCPort=50065
[ChatServer2]
name=ChatServer2
host=127.0.0.1
port=50056
RPCPort=50066

[Redis]
host=127.0.0.1
port=6379
password=38324
[Mysql]
host=127.0.0.1
port=3306
password=38324
schema=chat_server
user=root

最主要的是关于服务器的地方。首先解析ChatServers得到多个服务器的名称,在根据名称找到对应的详细的信息,比如host,port等。

那么对应的服务器的配置文件也要修改,以ChatServer1为例:

[GateWayServer]
port=9999
[VarifyServer]
host=127.0.0.1
port=50051
[StatusServer]
host=127.0.0.1
port=50052

[SelfServer]
name=ChatServer1
host=127.0.0.1
port=50055
RPCPort = 50065
[PeerServer]
servers=ChatServer2
[ChatServer2]
name=ChatServer2
host=127.0.0.1
port=50056
RPCPort=50066

[Redis]
host=127.0.0.1
port=6379
password=38324
[Mysql]
host=127.0.0.1
port=3306
password=38324
schema=chat_server
user=root

首先是SelfServer标注自己的信息,然后是PeerServer,我们通过解析这个,得到其他服务器的名称,在根据此,寻找对应的详细信息。类似StatusServer服务器的ChatServers.

  1. 全局文件的改动

我们修改了一些全局的标志,用于方便在redis中查找对应的数据

// const.h

#define EMAIL_PREFIX "email_"
#define USER_PREFIX "user_"
#define USERIP_PREFIX "uip_"
#define USER_TOKEN_PREFIX "user_token_"
#define LOGIN_COUNT_PREFIX "login_count_"
#define USER_BASE_INFO_PREFIX "user_base_info_"
#define IP_COUNT_PREFIX "ip_count_"
  1. 用户管理类UserManager(ChatServer)

主要是将用户的uid和对应的session绑定起来,通过uid可以方便的查找到对应会话session.

//UserManager.h
#ifndef USERMANAGER_H
#define USERMANAGER_H

#include "../redis/RedisManager.h"
#include "../mysql/MysqlManager.h"
#include "./Singleton.h"
#include "../session/Session.h"
#include <memory>
#include <mutex>
#include <unordered_map>

class UserManager:public Singleton<UserManager>
{
    friend class Singleton<UserManager>;
public:
    ~UserManager();
    std::shared_ptr<Session>GetSession(int uid);
    void SetUserSession(int uid,std::shared_ptr<Session>session);
    void RemoveUserSession(int uid);
private:
    UserManager();
    std::mutex _session_mutex;
    std::unordered_map<std::string,std::shared_ptr<Session>>_uid_with_session;
};



#endif



// UserManager.cpp
#include "UserManager.h"
#include <array>
#include <mutex>
#include <string>

UserManager::~UserManager(){
    _uid_with_session.clear();
}
std::shared_ptr<Session>UserManager::GetSession(int uid){
    auto uid_str = std::to_string(uid);
    {
        std::lock_guard<std::mutex>lock(_session_mutex);
        auto it = _uid_with_session.find(uid_str);
        if (it == _uid_with_session.end()){
            return nullptr;
        }
        return it->second;
    }
}
void UserManager::SetUserSession(int uid,std::shared_ptr<Session>session){
    auto uid_str = std::to_string(uid);
    {
        std::lock_guard<std::mutex>lock(_session_mutex);
        _uid_with_session[uid_str] = session;
    }
}
void UserManager::RemoveUserSession(int uid){
    auto uid_str = std::to_string(uid);
    {
        std::lock_guard<std::mutex>lock(_session_mutex);
        _uid_with_session.erase(uid_str);
    }
}
UserManager::UserManager(){

}

  1. ChatGrpcClient(ChatServer)

作为分布式的服务器,服务器之间一定要有通信,那么作为发起方角色就是客户端,因此我们编写请求grpc的客户端类。当然服务请求还没有真正实现,只是搭起了框架。

//h
#ifndef CHATGRPCCLIENT_H
#define CHATGRPCCLIENT_H

#include "../global/ConfigManager.h"
#include "../global/Singleton.h"
#include "../global/const.h"
#include "../data/UserInfo.h"
#include "RPCPool.h"
#include "message.grpc.pb.h"
#include "message.pb.h"

#include <nlohmann/json.hpp>
#include <grpcpp/grpcpp.h>
#include <grpcpp/support/status.h>
#include <unordered_map>


using grpc::Channel;
using grpc::Status;
using grpc::ClientContext;
using message::ChatServer;

using message::AddFriendRequest;
using message::AddFriendResponse;

using message::AuthFriendRequest;
using message::AuthFriendResponse;

using message::GetChatServerResponse;
using message::GetChatServerRequest;

using message::TextChatMessageRequest;
using message::TextChatMessageResponse;
using message::TextChatData;

class ChatGrpcClient : public Singleton<ChatGrpcClient> {
    friend class Singleton<ChatGrpcClient>;

public:
    ~ChatGrpcClient() = default;
    GetChatServerResponse NotifyAddFriend(std::string server_ip,const AddFriendRequest&);
    AuthFriendResponse NotifyAuthFriend(std::string server_ip,const AuthFriendRequest&);
    TextChatMessageResponse NotifyTextChatMessage(std::string server_ip,const TextChatMessageRequest&req,const json&);
    bool GetBaseInfo(std::string base_key,int uid,std::shared_ptr<UserInfo>&userinfo);

private:
    ChatGrpcClient();

    std::unordered_map<std::string,std::unique_ptr<RPCPool<ChatServer, ChatServer::Stub>>>_pool;
};



#endif



// cpp
#include "ChatGrpcClient.h"
#include "../global/ConfigManager.h"
#include "message.pb.h"
#include <new>
#include <string>


//TODO:
GetChatServerResponse ChatGrpcClient::NotifyAddFriend(std::string server_ip,const AddFriendRequest&){
    GetChatServerResponse rsp;
    return rsp;
}
AuthFriendResponse ChatGrpcClient::NotifyAuthFriend(std::string server_ip,const AuthFriendRequest&){
    AuthFriendResponse rsp;
    return rsp;
}
TextChatMessageResponse ChatGrpcClient::NotifyTextChatMessage(std::string server_ip,const TextChatMessageRequest&req,const json&){
    TextChatMessageResponse rsp;
    return rsp;
}
bool ChatGrpcClient::GetBaseInfo(std::string base_key,int uid,std::shared_ptr<UserInfo>&userinfo){
    return true;
}

ChatGrpcClient::ChatGrpcClient(){
    auto&cfg = ConfigManager::GetInstance();
    auto server_list = cfg["PeerServer"]["servers"];

    std::vector<std::string>words;
    words.reserve(10);

    std::stringstream ss(server_list);
    std::string word;

    while (std::getline(ss,word,',')){
        words.push_back(word);
    }

    for(const auto&word:words){
        if(cfg["word"]["name"].empty()){
            continue;
        }
        _pool[cfg[word]["name"]] = std::make_unique<RPCPool<ChatServer, ChatServer::Stub>>(10,cfg[word]["host"],cfg[word]["port"]);
    }
}

  1. ChatGrpcServer(ChatServer)

同理,接受grpc请求,我们的角色就是服务端,我们需要编写服务端的代码予以回复。同样的,服务并没有真正实现。

//h
#ifndef CHATGRPCSERVER_H
#define CHATGRPCSERVER_H

#include "../global/ConfigManager.h"
#include "../global/Singleton.h"
#include "../global/const.h"
#include "../data/UserInfo.h"
#include "RPCPool.h"
#include "message.grpc.pb.h"
#include "message.pb.h"

#include <grpcpp/server_context.h>
#include <nlohmann/json.hpp>
#include <grpcpp/grpcpp.h>
#include <grpcpp/support/status.h>
#include <unordered_map>


using grpc::Channel;
using grpc::Status;
using grpc::ClientContext;
using message::ChatServer;

using message::AddFriendRequest;
using message::AddFriendResponse;

using message::AuthFriendRequest;
using message::AuthFriendResponse;

using message::GetChatServerResponse;
using message::GetChatServerRequest;

using message::TextChatMessageRequest;
using message::TextChatMessageResponse;
using message::TextChatData;
class ChatGrpcServer final:public message::ChatServer::Service
{
public:
    ChatGrpcServer();
    Status NotifyAddFriend(grpc::ServerContext*context,const AddFriendRequest*request,AddFriendResponse*response)override;
    Status NotifyAuthFriend(grpc::ServerContext*context,const AuthFriendRequest*request,AuthFriendResponse*response)override;
    Status NotifyTextChatMessage(grpc::ServerContext*context,const TextChatMessageRequest*request,TextChatMessageResponse*response)override;
    bool GetBaseInfo(std::string base_key,int uid,std::shared_ptr<UserInfo>&userinfo);
private:
};

#endif

//cpp
#include "ChatGrpcServer.h"

ChatGrpcServer::ChatGrpcServer()
{
}
// TODO:
Status ChatGrpcServer::NotifyAddFriend(grpc::ServerContext* context, const AddFriendRequest* request, AddFriendResponse* response)
{
    return Status::OK;
}
Status ChatGrpcServer::NotifyAuthFriend(grpc::ServerContext* context, const AuthFriendRequest* request, AuthFriendResponse* response)
{
    return Status::OK;
}
Status ChatGrpcServer::NotifyTextChatMessage(grpc::ServerContext* context, const TextChatMessageRequest* request, TextChatMessageResponse* response)
{
    return Status::OK;
}
bool ChatGrpcServer::ChatGrpcServer::GetBaseInfo(std::string base_key, int uid, std::shared_ptr<UserInfo>& userinfo)
{
    return true;
}

  1. proto文件(All)

从上面的4,5可以看出proto文件一定做了很多的改动(这里给出了新增的,直接添加在之后):

message AddFriendRequest{
    int32 applyUid=1;
    string name=2;
    string desc = 3;
    int32 toUid=4;
}

message AddFriendResponse{
    int32 error=1;
    int32 applyUid=2;
    int32 toUid=3;
}

message ReplyFriendRequest{
    int32 replyUid = 1;
    bool agree = 2;
    int32 toUid=3;
}

message ReplyFriendResponse{
    int32 error=1;
    int32 replyUid=2;
    int32 toUid=3;
}

message SendChatMessageRequest{
    int32 fromUid = 1;
    int32 toUid = 2;
    string message = 3;
}

message SendChatMessageResponse{
    int32 error = 1;
    int32 fromUid = 2;
    int32 toUid = 3;
}

message AuthFriendRequest{
    int32 fromUid = 1;
    int32 toUid = 2;
}

message AuthFriendResponse{
    int32 error = 1;
    int32 fromUid = 2;
    int32 toUid = 3;
}

message TextChatData{
    string msgId = 1;
    string msgContent = 2;
}

message TextChatMessageRequest{
    int32 fromUid = 1;
    int32 toUid = 2;
    repeated TextChatData textMsgs = 3;
}
message TextChatMessageResponse{
    int32 error = 1;
    int32 fromUid = 2;
    int32 toUid = 3;
    repeated TextChatData textMsgs = 4;
}


service ChatServer{
    rpc NotifyAddFriend(AddFriendRequest)returns(AddFriendResponse){}
    rpc ReplyAddFriend(ReplyFriendRequest)returns(ReplyFriendResponse){}
    rpc SendChatMessage(SendChatMessageRequest)returns(SendChatMessageResponse){}
    rpc NotifyAuthFriend(AuthFriendRequest)returns(AuthFriendResponse){}
    rpc NotifyTextChatMessage(TextChatMessageRequest)returns(TextChatMessageResponse){}
}

由于每次改动都需要重新protoc编译这个protobuf文件,为了方便,我们编写脚本自动编译。

#!/bin/bash

set -euo pipefail

OUT_DIR=.

protoc --cpp_out="$OUT_DIR" \
    --grpc_out="$OUT_DIR" \
    --plugin=protoc-gen-grpc=$(which grpc_cpp_plugin) \
    ./*.proto
echo "protoc完毕"

  1. StausServideImpl改动(StatusServer)

这里最主要的改动,一句话:多用redis直接获取,而非grpc服务

之前我们过多了将内容局限在了一个中转服务器,每次其他的服务器获取都需要通过grpc,速度不快,也比较耦合。其次是过多的信息,比如用户的uid和token的对应情况,直接放在std::unordered_map中,现在转移到redis之中,线程安全,多服务共享,可以持久化,也更容易扩展。

下面直接给出改动之后的文件。

//h
#ifndef STATUSSERVICEIMPL_H
#define STATUSSERVICEIMPL_H

#include "message.grpc.pb.h"
#include <grpc++/grpc++.h>
#include <queue>
#include <unordered_map>

using grpc::Server;

struct ChatServer {
    std::string host;
    std::string port;
    std::string name;
    int con_count;
};

class StatusServiceImpl final : public message::StatusService::Service {
public:
    StatusServiceImpl();
    grpc::Status GetChatServer(grpc::ServerContext* context, const message::GetChatServerRequest* request, message::GetChatServerResponse* response) override;
    grpc::Status Login(grpc::ServerContext* context, const message::LoginRequest* request, message::LoginResponse* response) override;

private:
    void insertToken(int uid, const std::string& token);
    ChatServer GetChatServer();

private:
    struct CompareServers {
        bool operator()(const ChatServer& a, const ChatServer& b) const
        {
            return a.con_count < b.con_count;
        }
    };

    // std::priority_queue<ChatServer, std::vector<ChatServer>, CompareServers> _servers;
    std::unordered_map<std::string, ChatServer>_servers;
    std::mutex _server_mutex;

};

#endif


//cpp
#include "StatusServiceImpl.h"
#include "../global/ConfigManager.h"
#include "../global/const.h"
#include "../redis/RedisManager.h"
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <mutex>
#include <spdlog/spdlog.h>

std::string generate_unique_string()
{
    boost::uuids::random_generator gen;
    boost::uuids::uuid uuid = gen();
    return boost::uuids::to_string(uuid);
}

grpc::Status StatusServiceImpl::GetChatServer(grpc::ServerContext* context, const message::GetChatServerRequest* request, message::GetChatServerResponse* response)
{
    std::string prefix("ChatServer received :");
    const auto& server = GetChatServer();
    response->set_host(server.host);
    response->set_port(server.port);
    response->set_error(static_cast<int>(ErrorCodes::SUCCESS));
    response->set_token(generate_unique_string());
    insertToken(request->uid(), response->token());
    SPDLOG_INFO("{} uid:{}, token:{}, host:{}, port:{}", prefix, request->uid(), response->token(), server.host, server.port);
    return grpc::Status::OK;
}

void StatusServiceImpl::insertToken(int uid, const std::string& token)
{
    std::string token_key = USER_TOKEN_PREFIX + std::to_string(uid);
    RedisManager::GetInstance()->Set(token_key,token);
}

ChatServer StatusServiceImpl::GetChatServer()
{
    std::lock_guard<std::mutex>lock(_server_mutex);
    auto minServer = _servers.begin()->second;
    auto count_str = RedisManager::GetInstance()->HGet(LOGIN_COUNT_PREFIX,minServer.name);
    if (count_str.empty()){
        minServer.con_count = INT_MAX;
    }else{
        minServer.con_count = std::stoi(count_str);
    }

    for(auto&[name,server]:_servers){
        if (name == minServer.name){
            continue;
        }

        auto count_str = RedisManager::GetInstance()->HGet(LOGIN_COUNT_PREFIX,name);
        if (count_str.empty()){
            server.con_count = INT_MAX;
        }else{
            server.con_count = std::stoi(count_str);
        }

        if (server.con_count < minServer.con_count) {
            minServer = server;
        }
    }
    return minServer;
}

grpc::Status StatusServiceImpl::Login(grpc::ServerContext* context, const message::LoginRequest* request, message::LoginResponse* response)
{
    auto uid = request->uid();
    auto token = request->token();

    std::string uid_str = std::to_string(uid);
    std::string token_key = USER_TOKEN_PREFIX + uid_str;
    std::string token_value = "";
    bool success = RedisManager::GetInstance()->Get(token_key,token_value);
    if (success){
        response->set_error(static_cast<int>(ErrorCodes::ERROR_UID_INVALID));
        return grpc::Status::OK;
    }else{
        response->set_error(static_cast<int>(ErrorCodes::ERROR_TOKEN_INVALID));
        return grpc::Status::OK;
    }

    response->set_error(static_cast<int>(ErrorCodes::SUCCESS));
    response->set_uid(uid);
    response->set_token(token);
    return grpc::Status::OK;
}

StatusServiceImpl::StatusServiceImpl()
{
    auto& cfg = ConfigManager::GetInstance();
    auto server_list = cfg["ChatServers"]["name"];

    std::vector<std::string>words;
    words.reserve(10);

    std::stringstream ss(server_list);
    std::string word;

    while(std::getline(ss,word,',')){
        words.push_back(word);
    }

    for(auto&word:words){
        if (cfg[word]["name"].empty()){
            continue;
        }

        ChatServer server;
        server.host = cfg[word]["host"];
        server.port = cfg[word]["port"];
        server.name = cfg[word]["name"];
        _servers[server.name] = server;
    }
    SPDLOG_INFO("size:{}",_servers.size());

}

StatusServiceImpl中直接从配置文件自动获取服务器的信息,添加到哈希表中,不需要手动的push,更容易扩展。

GetChatServer直接通过redis获取压力最小的服务器。

同理,InsertToken也使用redis操作。

实际上Login的作用并不大了,已经被redis的直接解决了。但是考虑到GateWayServer还有Login调用grpc服务,我们仍保留使用。

  1. LoginSystem(ChatServer)

同理使用redis进行token的验证,信息的获取,如果验证成功,连接上服务器,同时修改服务器的连接数+1。

  • 首先是redis获取角色详细信息的函数GetBaseInfo,我们补充了userinfo的内容,包括了头像,性别,备注等等。如果redis一次获取到直接返回信息,如果没有就mysql再数据库查询,查询完毕再加入redis方便下次查询。
bool LogicSystem::GetBaseInfo(std::string base_key, int uid, std::shared_ptr<UserInfo>& userinfo)
{
    std::string info_str = "";
    bool b_base = RedisManager::GetInstance()->Get(base_key, info_str);
    if (b_base) {
        json j = json::parse(info_str);
        userinfo->name = j["name"].get<std::string>();
        userinfo->email = j["email"].get<std::string>();
        userinfo->uid = j["uid"].get<int>();
        userinfo->sex = j["sex"].get<int>();
        userinfo->nick = j["nick"].get<std::string>();
        userinfo->desc = j["desc"].get<std::string>();
        userinfo->icon = j["icon"].get<std::string>();
        SPDLOG_INFO("uid:{},name:{},email:{}", uid, userinfo->name, userinfo->email);
    } else {
        userinfo = MysqlManager::GetInstance()->GetUser(uid);
        if (userinfo == nullptr) {
            return false;
        }
        json j;
        j["name"] = userinfo->name;
        j["email"] = userinfo->email;
        j["uid"] = userinfo->uid;
        j["sex"] = userinfo->sex;
        j["nick"] = userinfo->nick;
        j["desc"] = userinfo->desc;
        j["icon"] = userinfo->icon;
        RedisManager::GetInstance()->Set(base_key, j.dump());
    }
    return true;
}
  • 然后是注册的回调函数,我们目前只有客户端对服务器登陆请求的回调。我们直接redis获取token进行验证,成功就去获取角色信息,一切成功,我们修改连接服务器的数量,返回请求内容。
void LogicSystem::RegisterCallBacks()
{
    // 登陆请求
    _function_callbacks[MsgId::ID_CHAT_LOGIN] = [this](std::shared_ptr<Session> session, uint16_t msg_id, const std::string& msg) {
        json j = json::parse(msg);
        auto uid = j["uid"].get<int>();
        auto token = j["token"].get<std::string>();
        SPDLOG_INFO("Thread: {},User {} Login with token {}", thread_id_to_string(std::this_thread::get_id()), uid, token);

        json jj;
        Defer defer([this, &jj, session]() {
            std::string return_str = jj.dump();
            session->Send(return_str, static_cast<int>(MsgId::ID_CHAT_LOGIN_RSP));
        });

        std::string uid_str = std::to_string(uid);
        std::string token_key = USER_TOKEN_PREFIX + uid_str;
        std::string token_value = "";

        bool success = RedisManager::GetInstance()->Get(token_key, token_value);
        if (!success) {
            jj["error"] = ErrorCodes::ERROR_UID_INVALID;
            return;
        }
        if (token_value != token) {
            jj["error"] = ErrorCodes::ERROR_TOKEN_INVALID;
            return;
        }

        std::string base_key = USER_BASE_INFO_PREFIX + uid_str;
        auto user_info = std::make_shared<UserInfo>();
        bool b_base = GetBaseInfo(base_key, uid, user_info);
        if (!b_base) {
            jj["error"] = ErrorCodes::ERROR_UID_INVALID;
            return;
        }

        jj["error"] = ErrorCodes::SUCCESS;

        jj["uid"] = uid;
        jj["name"] = user_info->name;
        jj["email"] = user_info->email;
        jj["nick"] = user_info->nick;
        jj["sex"] = user_info->sex;
        jj["desc"] = user_info->desc;
        jj["icon"] = user_info->icon;
        jj["token"] = token;

        // 获取消息列表
        // 获取好友列表

        // 更新登陆数量
        auto server_name = ConfigManager::GetInstance()["SelfServer"]["name"];
        auto count_str = RedisManager::GetInstance()->HGet(LOGIN_COUNT_PREFIX, server_name);
        int count = 0;
        if (!count_str.empty()) {
            count = std::stoi(count_str);
        }
        count++;
        count_str = std::to_string(count);
        RedisManager::GetInstance()->HSet(LOGIN_COUNT_PREFIX, server_name, count_str);

        // session绑定uid
        session->SetUid(uid);
        // 绑定连接的服务器名称和用户uid
        std::string ip_key = USERIP_PREFIX + std::to_string(uid);
        RedisManager::GetInstance()->Set(ip_key, server_name);
        // uid和session绑定管理,方便之后踢人
        UserManager::GetInstance()->SetUserSession(uid, session);
    };
}
  1. Main(ChatServer)
  • 我们是在ChatServer中加入了ChatGrpcServer,但是还没有启动,因此我们需要在main中单独开一个线程进行服务的启动。
 std::string server_address = cfg["SelfServer"]["host"]+":"+cfg["SelfServer"]["RPCPort"];
        ChatGrpcServer service;
        grpc::ServerBuilder builder;
        builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
        builder.RegisterService(&service);
        std::unique_ptr<grpc::Server>server(builder.BuildAndStart());
        SPDLOG_INFO("Grpc Server On: {}",server_address);

        std::thread grpc_server([&server](){
            server->Wait();
        });


        auto pool = AsioPool::GetInstance();
        boost::asio::io_context ioc;
        boost::asio::signal_set signals(ioc, SIGINT, SIGTERM);
        signals.async_wait([&ioc, pool,&server](const boost::system::error_code& /*error*/, int /*signal_number*/) {
            pool->Stop();
            ioc.stop();
            server->Shutdown();
        });

        auto port = cfg["SelfServer"]["port"];
        std::make_shared<Server>(ioc, std::stoi(port))->Start();
        ioc.run();


        RedisManager::GetInstance()->HDel(LOGIN_COUNT_PREFIX,server_name);
        RedisManager::GetInstance()->Close();
        grpc_server.join();

重点模板绑定,监听,运行。最后别忘了在signals处理中Shutdown,以及最后线程的join.

  • 在StatusServer里面,关于寻找压力最小的服务器的操作中,对于redis没有得到数量的服务器的结果,我们需要设置为INT_MAX代表异常:

auto count_str = RedisManager::GetInstance()->HGet(LOGIN_COUNT_PREFIX,name);
if (count_str.empty()){
server.con_count = INT_MAX;
}else{
server.con_count = std::stoi(count_str);
}

这就有一个问题,所有的服务器开启之后都是默认没有的,也就是redis查询结果为空的。为此我们需要在服务器启动之后,主动设置一下这个参数,否则会导致一直选用某一个服务器,也就丧失了分布式的意义。

auto server_name = cfg["SelfServer"]["name"];

RedisManager::GetInstance()->HSet(LOGIN_COUNT_PREFIX,server_name,"0");
posted @ 2025-12-24 23:17  大胖熊哈  阅读(1)  评论(0)    收藏  举报