基于C++20协程与protobuf的轻量级、高性能Rpc框架

本文介绍了基于C++20协程和protobuf的轻量级、高性能Rpc框架,旨在简化现有框架的复杂性。文章通过实例展示了如何定义rpc方法、客户端和服务端的调用,并探讨了核心实现,包括ConnMgr连接管理器、RpcMgr Rpc管理器和RpcCoroMgr协程管理器。此外,还讨论了Tcp通信、协程的性能优势以及框架的性能测试预期。
摘要由CSDN通过智能技术生成

1.前言

最近因为学习需要,了解了当前一些基于C++20协程的Rpc实现方案,但现有的框架基本上代码量都比较大、框架比较复杂难懂、文档介绍较少。在一个Rpc调用过程中,涉及到多个协程的层层嵌套,影响了整体性能和可读性。因此,自己动手设计并实现了一个轻量级、简单、易于理解的Rpc实现方案。框架基于C++20无栈协程与protobuf,代码量较小但实现了Rpc的所有的基础功能,适合用来学习或以此为基础进行无栈协程的Rpc开发。该框架每一个Rpc调用只会创建一个协程,避免了层层创建,提高性能。框架所有代码均已开源,见cpp20coroutine-protobuf-rpc

2.使用示例

话不多说,先看使用示例

2.1 rpc方法定义

根据pb的格式定义req和rsp协议、rpc服务EchoService、Echo和RelayEcho两个rpc方法,定义如下。使用Google的protoc即生成对应的给客户端调用的stub和给服务器调用的service代码。

message EchoRequest {
    optional string msg = 1;
}
message EchoResponse {
    optional string msg = 2;
}

service EchoService {
    rpc Echo(EchoRequest) returns (EchoResponse);
    rpc RelayEcho(EchoRequest) returns (EchoResponse);
}

2.2 客户端rpc调用示例:

// 协程方法定义
RpcCoro CallMeathod() {
  // 1.获取 or 创建rpc channel
  RpcChannel *channel = s_ConnMgr->GetRpcChannel("127.0.0.1", 6688);
  
  // 2.创建rpc req、rsp
  echo::EchoRequest req;
  req.set_msg("Hello, Echo.");
  echo::EchoResponse rsp;
  
  // 3.获取当前协程handle并构造proto controller
  MyController cntl(co_await GetHandleAwaiter{});
  
  // 4.构造proto rpc stub并调用其生成的rpc方法Echo,然后挂起协程等待返回
  echo::EchoService_Stub stub(channel);
  stub.Echo(cntl, req, rsp, nullptr);
  co_await std::suspend_always{};
  
  // 5.收到rsp包或出现超时等错误时,协程被唤醒,从proto controller中可以读取rpc调用状态,如果调用成功,此时可以读取到协程返回值rsp,打印协程返回结果
  LLOG("Recv Echo Rsp, status:%s, rsp:%s", cntl.Failed() ? cntl.ErrorText().c_str() : "success", 
       rsp.msg().c_str());
  
  // 协程内可再进行其他rpc调用并获取返回...
  stub.RelayEcho(cntl, req, rsp, nullptr);
  co_await std::suspend_always{};
  LLOG("Recv RelayEcho Rsp, status:%s, rsp:%s", cntl.Failed() ? cntl.ErrorText().c_str() : "success", 
       rsp.msg().c_str());
  
  co_return;
}

代码的详细流程及介绍如上所示,在第4步的调用protobuf生成的stub.Echo方法完成rpc的调用,内部rpc的调用过程中不会再产生额外的协程,stub.Echo方法只是完成数据包发送并记录上下文信息即返回了。返回后在co_await std::suspend_always{}处挂起等待,直到收到rpc返回包或者出现超时等错误时才会被唤醒,整个Rpc调用流程只会创建这一个协程。

2.3 服务端使用示例:

// 继承并实现pb生成的echo::EchoService,重写rpc方法Echo、RelayEcho
class MyEchoService : public echo::EchoService {
public:
  void Echo(::google::protobuf::RpcController *controller,
            const ::echo::EchoRequest *request, ::echo::EchoResponse *response,
            ::google::protobuf::Closure *done) override;
  void RelayEcho(::google::protobuf::RpcController *controller,
             const ::echo::EchoRequest *request, ::echo::EchoResponse *response,
             ::google::protobuf::Closure *done) override;
};

// 在Echo方法中,根据rpc请求rsp填写response,然后return即可,框架内部会完成回包逻辑
void MyEchoService::Echo(::google::protobuf::RpcController *controller,
                         const ::echo::EchoRequest *request,
                         ::echo::EchoResponse *response,
                         ::google::protobuf::Closure *done) {
  // 填充协程返回值
  response->set_msg(std::string(" Echo >>>>>>> ") + request->msg());
  done->Run();
}

// RelayEcho方法中,涉及到内嵌rpc调用,所以创建新协程InnerCallMeathod来完成
void MyEchoService::RelayEcho(::google::protobuf::RpcController *controller,
                          const ::echo::EchoRequest *req,
                          ::echo::EchoResponse *rsp,
                          ::google::protobuf::Closure *done) {
  LLOG(nullptr, nullptr, LLBC_LogLevel::Trace, "RECEIVED MSG: %s",
       req->msg().c_str());

  InnerCallMeathod(controller, req, rsp, done);
}

// InnerCallMeathod协程
RpcCoro InnerCallMeathod(::google::protobuf::RpcController *controller,
                         const ::echo::EchoRequest *req,
                         ::echo::EchoResponse *rsp,
                         ::google::protobuf::Closure *done) {
  // 初始化内部 rpc innerReq innerRsp
  echo::EchoRequest innerReq;
  innerReq.set_msg("Relay Call >>>>>>" + req->msg());
  echo::EchoResponse innerRsp;
  
  // 获取 rpc channel
  RpcChannel *channel = s_ConnMgr->GetRpcChannel("127.0.0.1", 6699);

  // 内部 rpc 调用后挂起
  MyController cntl(co_await GetHandleAwaiter{});
  cntl.SetPtrParam(handle);
  echo::EchoService_Stub stub(channel);
  stub.Echo(cntl, innerReq, innerRsp, nullptr);
  co_await std::suspend_always{};
  
  // 收到rsp包或出现超时等错误时,此协程被唤醒,
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值