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;
};

浙公网安备 33010602011771号