如何从 protobuf 构建 rpc?

Posted

技术标签:

【中文标题】如何从 protobuf 构建 rpc?【英文标题】:how to build rpc from protobuf? 【发布时间】:2019-12-04 17:09:59 【问题描述】:

我正在用 boost::asio 和 protobuf 编写“筏共识算法”。服务器通过使用两种类型的“RPC”相互通信:AppendEntryRPC 和 RequestVoteRPC。当服务器 A 从服务器 B 接收到一个已知长度的字符串时,它如何知道该字符串应该被解码为哪种 RPC 结构?

我知道这个问题有一个简单的解决方案,将接收阶段分为两个阶段:第一阶段获取 RPC 类型名称,第二阶段获取 RPC 字符串内容然后对其进行解码。但我只是想避免这样做。有什么解决办法吗?

我还知道有一个名为“grpc”的框架,但我无法在我的 Mac 上成功运行它的示例。或者有人可以用天真的语言解释“grpc”如何解决这个问题吗?

【问题讨论】:

【参考方案1】:

您可以创建一个额外的消息,它可以包含这两种类型的消息并且始终可以安全地解析:

message Vote 
  ...


message Entry 
  ...


message VoteOrEntry 
  oneof combined 
    Vote vote = 1;
    Entry entry = 2;
  

然后使用has_vote()has_entry() 来区分您的情况。

您仍然只会收到一封VoteOrEntry 类型的消息。对于您在上面添加的新示例项目,这个write.cc 应该适用于工作:

#include <ctime>
#include <fstream>
#include <google/protobuf/util/time_util.h>
#include <iostream>
#include <string>

#include "test.pb.h"

using namespace std;

using google::protobuf::util::TimeUtil;

int main(int argc, char *argv[]) 
    GOOGLE_PROTOBUF_VERIFY_VERSION;

    if (argc != 2) 
        cerr << "Usage:  " << argv[0] << " ADDRESS_BOOK_FILE" << endl;
        return -1;
    

    cap cap;
    int type;
    cout << "Please choose a type [1, 2]: ";
    cin >> type;

    if (type == 1) 
        type1 *t1 = cap.mutable_entity1();
        string name;
        cout << "Please choose a name: ";
        cin >> name;
        int id;
        cout << "Please choose an id: ";
        cin >> id;
        t1->set_name(name);
        t1->set_id(id);

        fstream output(argv[1], ios::out | ios::trunc | ios::binary);
        if(!cap.SerializeToOstream(&output))
            cerr<<"failed to write to file"<<endl;
            return -1;
        
    else
      type2 *t2 = cap.mutable_entity2();
      int id;
      cout << "Please choose an id: ";      
      cin>>id;
      string name;
      cout << "Please choose a name: ";
      cin>>name;
      int v;
      cout << "Please choose v: ";
      cin>>v;
      t2->set_name(name);
      t2->set_id(id);
      t2->set_v(v);
      fstream output(argv[1], ios::out | ios::trunc | ios::binary);
      if(!cap.SerializeToOstream(&output))
          cerr<<"failed to write to file"<<endl;
          return -1;
      
    

    google::protobuf::ShutdownProtobufLibrary();

    return 0;

【讨论】:

这是可能的,但有限制,RPC 大多具有不同的结构字段等。将其用于错误或状态处理是有意义的,因为它应该为各方标准化。 谢谢,“oneof combied”其实是我要找的,虽然听不懂你们之间的对话。 @Jan-Gerd。兄弟,你能举个例子吗?因为我不能像官方的“地址簿”示例(尤其是 write)那样编写示例程序“writer and reader”。我也不能谷歌它。 "write(37139,0x10a25f5c0) malloc: *** 对象 0x7f8cfe500000 错误:未分配指针被释放 write(37139,0x10a25f5c0) malloc: *** 在 malloc_error_break 中设置断点进行调试"它一直抱怨 set_allocated_vote 或 set_allocated_entry @YNX,请将您的代码发布到某个地方,然后我可以看看。如果这不再与 RPC 相关,请为其创建一个新问题。【参考方案2】:

不幸的是,没有解决方案。服务器无法识别消息类型,必须提前知道。因为每条消息/RPC 都可以有一个不同的 protobuff proto 文件来反序列化。

最好的方法是构建一个具有已知结构的标头系统,这样您就知道要反序列化什么。

【讨论】:

以上是关于如何从 protobuf 构建 rpc?的主要内容,如果未能解决你的问题,请参考以下文章

如何从 protobuf (.proto) 文件中生成 (.json/.yaml) 中的 swagger3 (OpenAPI3) 规范?

Protobuf-Net:实现服务器、rpc 控制器和 rpc 通道

如何使用 mingw 在 Windows 中构建 protobuf-c?

从不同的项目.Net访问protobuf

如何使用 protobuf-net 处理 .proto 文件

protobuf及grpc的client请求