Chap06-GateWayServerImproveConcurrencyCapacity

Chap06-GateWayServerImproveConcurrencyCapacity

提高前后端通信的并发

原本我们只有一个io_context用来执行所有的操作,包括连接和读写操作。而现在我们要提高他的并发能力,我们就可以创建多个io_context.其中一个是在main函数中创建并且传递给GateWayServer,用于接受连接。

然后我们添加AsioPool的类,这其中保存有一定数量(默认是std:🧵:hardware_concurrency)的io_context.

#ifndef ASIOPOOL_H
#define ASIOPOOL_H

#include "Singleton.h"
#include <boost/asio.hpp>
#include <boost/asio/executor_work_guard.hpp>
#include <thread>
#include <vector>

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

public:
    ~AsioPool();
    AsioPool(const AsioPool&) = delete;
    AsioPool& operator=(const AsioPool&) = delete;

    boost::asio::io_context& GetIOService();
    void Stop();

private:
    AsioPool(std::size_t size = std::thread::hardware_concurrency());

private:
    std::vector<boost::asio::io_context> _services;
    std::vector<std::thread> _threads;
    std::vector<std::unique_ptr<boost::asio::executor_work_guard<boost::asio::io_context::executor_type>>> _works;
    std::size_t _next_services;
};

#endif

然后具体在GateWayServer接收到连接之后,需要创建一个Session对话进行与客户端的对话通信。这时候我们就可以从AsioPool中得到一个io_context用于通信。

void GateWayServer::Start()
{
    auto& io_context = AsioPool::GetInstance()->GetIOService();
    std::shared_ptr<Session> conn = std::make_shared<Session>(io_context);
    _acceptor.async_accept(conn->GetSocket(), [this, conn, self = shared_from_this()](const boost::system::error_code& ec) {
        try {
            if (ec) {
                self->Start();
                return;
            }
            conn->Start();
            self->Start();
        } catch (std::exception& e) {
            std::cerr << "Exception: " << e.what() << std::endl;
        }
    });
}

这样的话,Session就不是接受一个创建好的socket了,而是接受一个取出的io_context,然后自己创建一个socket,自己保存。

Session::Session(boost::asio::io_context& ioc)
    : _socket(ioc)
{
}

提高后端grpc之间的并发

channel就相当于电话线,stub就相当于客服。

不建议创建多个channel,因为创建成本高,需复用。就像你不会每次打电话都重新铺一条电话线到客服中心。你应该保持这条线路长期畅通,让所有通话共享。

而stub支持线程安全的并发,一个即可。不过多个也无妨,因为创建成本低,可按需创建。就像每次有业务需要咨询时,客服中心会从空闲的客服中分配一个给你。这个客服为你服务完后,可以被分配给下一个人。你甚至可以同时和多个客服通话(多线程使用多个Stub),只要他们共用同一条电话线(Channel)。

因此我们创建了一个RPCPool

using message::GetSecurityCodeRequest;
using message::GetSecurityCodeResponse;
using message::VarifyService;

class RPCPool {
public:
    RPCPool(std::size_t size, const std::string& host, const std::string& port)
        : _size(size)
        , _host(host)
        , _port(port)
        , _stop(false)
    {
        _channel = grpc::CreateChannel(host + ":" + port, grpc::InsecureChannelCredentials());
        for (std::size_t i = 0; i < size; ++i) {
            _connections.push(VarifyService::NewStub(_channel));
        }
    }

    void Close()
    {
        _stop = true;
        _cv.notify_all();
    }

    ~RPCPool()
    {
        std::lock_guard<std::mutex> lock(_mutex);
        Close();
        while (!_connections.empty()) {
            _connections.pop();
        }
    }

    std::unique_ptr<VarifyService::Stub> GetConnection()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _cv.wait(lock, [this]() {
            return _stop || !_connections.empty();
        });
        if (_stop) {
            return nullptr;
        }

        auto context = std::move(_connections.front());
        _connections.pop();
        return context;
    }

    void backConnection(std::unique_ptr<VarifyService::Stub> context)
    {
        std::lock_guard<std::mutex> lock(_mutex);
        if (_stop) {
            return;
        }
        _connections.push(std::move(context));
        _cv.notify_one();
    }

private:
    std::shared_ptr<grpc::Channel> _channel;
    std::atomic<bool> _stop;
    std::size_t _size;
    std::string _host;
    std::string _port;
    std::queue<std::unique_ptr<VarifyService::Stub>> _connections;
    std::mutex _mutex;
    std::condition_variable _cv;
};

同时修改我们的VerifyClient的结构。不再是直接使用某个channel和stub,而是只从RPCPool取出一个stub进行通信。

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

public:
    GetSecurityCodeResponse GetSecurityCode(const std::string& email)
    {
        grpc::ClientContext context;
        GetSecurityCodeRequest request;
        GetSecurityCodeResponse response;
        request.set_email(email);

        auto stub = _pool->GetConnection();
        grpc::Status status = stub->GetSecurityCode(&context, request, &response);

        if (!status.ok()) {
            response.set_error(static_cast<int>(ErrorCodes::RPCFAILED));
        }
        _pool->backConnection(std::move(stub));

        return response;
    }

private:
    VerifyClient()
    {
        auto& cfgMgr = ConfigManager::GetInstance();
        std::string host = cfgMgr["VarifyServer"]["host"];
        std::string port = cfgMgr["VarifyServer"]["port"];
        _pool = std::make_unique<RPCPool>(10, host, port);
    }

    std::unique_ptr<RPCPool> _pool;
};
posted @ 2025-12-24 23:16  大胖熊哈  阅读(3)  评论(0)    收藏  举报