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

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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Protobuf-rpc是一种基于protobuf的远程方法调用(RPC)框架。它提供了服务器端和客户端的实现,其中服务器端仅支持Java,而客户端则支持Objective-C和Java。RPC是一种封装了网络协议和序列化、反序列化功能的通信框架,而protobuf-rpc使用protobuf实现了序列化和反序列化的功能。通过protobuf-rpc,客户端可以像调用本地方法一样调用远程接口方法,实现了透明调用机制,让使用者不必显示区分本地调用和远程调用。这使得开发人员可以很方便地在分布式系统中进行远程方法调用。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [protobuf-rpc:protobuf-rpc 是一个基于 Google ProtocolBuffer 的 RPC 框架。 目前 protobuf-rpc 支持 ...](https://download.csdn.net/download/weixin_42123296/19257793)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [protobuf+RPC技术](https://blog.csdn.net/weixin_27015375/article/details/114350163)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [python如何通过protobuf实现rpc](https://download.csdn.net/download/weixin_38599545/13771570)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值