c++ - 我如何利用 ZeroMQ 编写自己的 Protocol Buffers RPC 实现

标签 c++ protocol-buffers zeromq rpc

根据“Defining Services”下的 Google Protocol Buffers 文档”他们说,

it's also possible to use protocol buffers with your own RPC implementation.

据我了解,Protocol Buffers 本身并不实现 RPC。相反,它们提供了一系列必须由用户(那就是我!)实现的抽象接口(interface)。所以我想利用 ZeroMQ 来实现这些抽象接口(interface)来进行网络通信。

我正在尝试使用 ZeroMQ 创建一个 RPC 实现,因为我正在开发的项目已经实现了 ZeroMQ 来进行基本消息传递(因此我使用 gRPC,正如文档所建议的那样) 。

彻底阅读了proto文档后,我发现我必须实现抽象接口(interface)RpcChannelRpcController用于我自己的实现。

我已经构建了一个最小化的示例,展示了我目前的 RPC 实现情况

.proto 文件:为简洁起见省略了 SearchRequest 和 SearchResponse 架构

service SearchService {
    rpc Search (SearchRequest) returns (SearchResponse);
}

SearchServiceImpl.h:

class SearchServiceImpl : public SearchService {
 public:
  void Search(google::protobuf::RpcController *controller,
                    const SearchRequest *request,
                    SearchResponse *response,
                    google::protobuf::Closure *done) override {
    // Static function that processes the request and gets the result
    SearchResponse res = GetSearchResult(request);

    // Call the callback function
    if (done != NULL) {
    done->Run();
    }
    }
  }
};

MyRPCController.h:

class MyRPCController : public google::protobuf::RpcController {
 public:
    MyRPCController();

    void Reset() override;

    bool Failed() const override;

    std::string ErrorText() const override;

    void StartCancel() override;

    void SetFailed(const std::string &reason) override;

    bool IsCanceled() const override;

    void NotifyOnCancel(google::protobuf::Closure *callback) override;
 private:
  bool failed_;
  std::string message_;
};

MyRPCController.cpp - 基于this

void MyRPCController::Reset() {  failed_ = false; }

bool MyRPCController::Failed() const { return failed_; }

std::string MyRPCController::ErrorText() const { return message_; }

void MyRPCController::StartCancel() { }

void MyRPCController::SetFailed(const std::string &reason) {
  failed_ = true;
  message_ = reason;
}

bool MyRPCController::IsCanceled() const { return false; }

void MyRPCController::NotifyOnCancel(google::protobuf::Closure *callback) { }

MyRPCController::ChiRpcController() : RpcController() { Reset(); }

MyRpcChannel.h:

class MyRPCChannel: public google::protobuf::RpcChannel {
 public:
    void CallMethod(const google::protobuf::MethodDescriptor *method, google::protobuf::RpcController *controller,
                    const google::protobuf::Message *request, google::protobuf::Message *response,
                    google::protobuf::Closure *done) override;
};

到目前为止,我对示例的疑问:

  • 我应该在哪里将 ZeroMQ 放入其中?
    • 看起来它应该进入 RPCChannel,因为在我看到的示例中(参见第三个代码块 here ),它们传递了一个具有要绑定(bind)的端口的字符串(即 MyRpcChannel Channel("rpc :主机名:1234/myservice");)
  • 我关心我的 RPCController 实现,它看起来太简单了。应该有更多人去这里吗?
  • 如何实现RPCChannel,它看起来与SearchServiceImpl非常相似。这些类中的 1 个虚函数具有非常相似的方法签名,只是它是通用的。

以下是我遇到的其他一些 Stack Overflow 问题,其中包含有关该主题的一些有用信息:

  1. Protobuf-Net: implementing server, rpc controller and rpc channel - 这是我找到 RPCController 实现示例的地方。
  2. Using Protocol Buffers for implementing RPC in ZeroMQ - 这个答案很有趣,因为在最上面的答案中,他们似乎建议不要使用以 RPC 格式构建的 Protobufs 来处理 .proto 文件。
    • 我也在this中注意到了同样的概念。文件,位于名为 libpbrpc 的存储库中这似乎是示例代码的一个很好的来源
  3. 我可以/我应该使用现有的实现吗,例如 RPCZ

感谢您的帮助。我希望我提供了足够的信息并且清楚我在寻找什么。如果有不清楚或缺乏信息,请告诉我。我很乐意相应地编辑问题。

最佳答案

  • ZeroMQ 提供了一个低级 API,用于基于可包含任何数据的消息的网络通信。
  • ProtoBuffers 是一个将结构化数据编码为压缩二进制数据并对此类数据进行解码的库。
  • gRPC 是一个 RPC 框架,它为基于网络通信的 RPC 服务生成代码,并具有以 ProtoBuffers 数据形式交换数据的功能。

ZeroMQ 和 gRPC 都提供对网络通信的支持,但方式不同。您必须选择 ZeroMQ 或 gRPC 进行网络通信。 如果您选择 ZeroMQ,则可以使用交换二进制结构化数据的 ProtoBuffers 对消息进行编码。

要点是 ProtoBuffers 库允许对变体记录(类似于 C/C++ union )进行编码和解码,可以完全模拟具有交换 ProtoBuffers 消息功能的 RPC 服务提供的功能。

所以选项是:

  1. 将 ZeroMQ 与发送和接收原语以及 ProtoBuffers 编码的变体消息结合使用,这些消息可以包含各种子消息,例如
union Request
{
  byte msgType;
  MessageType1 msg1;
  MessageType2 msg2;
  MessageType3 msg3;
}

union Response
{
  byte msgType;
  MessageType3 msg1;
  MessageType4 msg2;
  MessageType5 msg3;
}

send(Request request);
receive(Response response);
  • 使用 gRPC 生成具有以下功能的服务
  • service MyService 
    {
      rpc function1(MessageType1) returns (Response);
      rpc function2(MessageType2) returns (Response);
      rpc function3(MessageType3) returns (Response);
    
      rpc functionN(MessageType3) returns (MessageType5);
    }
    

    (这里可以使用很多组合)

  • 仅使用单功能 gRPC 服务,例如
  • service MyService 
    {
        rpc function(Request) returns (Response);
    }
    

    该选项可能取决于

    对于第一个选项,与第二个选项相比,您必须做很多事情。您必须将发送的消息类型与预期接收的消息类型相匹配。

    如果其他人开发客户端,第二个选项将允许更容易/更快地理解所提供服务的功能。

    为了在 ZeroMQ 上开发 RPC 服务,我将定义这样的 .proto 文件,指定函数、参数(所有可能的输入和输出参数)和错误,如下所示:

    enum Function 
    {
        F1 = 0;
        F2 = 1;
        F3 = 2;
    }
    
    enum Error 
    {
        E1 = 0;
        E2 = 1;
        E3 = 2;
    }
    
    message Request
    { 
        required Function function = 1;
        repeated Input data = 2;
    }
    
    message Response
    { 
        required Function function = 1;
        required Error error = 2;
        repeated Output data = 3;
    }
    
    message Input
    { 
        optional Input1 data1 = 1;
        optional Input2 data2 = 2;
        ...
        optional InputN dataN = n;
    }
    
    message Output
    { 
        optional Output1 data1 = 1;
        optional Output2 data2 = 2;
        ...
        optional OutputN dataN = n;
    }
    
    message Message
    {
       repeated Request requests;
       repeated Response responses;
    }
    

    并且根据函数 ID,在运行时必须检查参数的数量和类型。

    关于c++ - 我如何利用 ZeroMQ 编写自己的 Protocol Buffers RPC 实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59616929/

    相关文章:

    c++ - constexpr 函数和硬编码参数

    C++,将非指针类型分配给模板类的成员指针

    c++ - 在 DEBUG 模式下调整字节字段大小时发生访问冲突

    c++ - 在 CMake 中使用 protobuf 作为 ExternalProject

    c++ - 如何读取 google::protobuf::io::CodedOutputStream::WriteVarint32ToArray 写入的数据

    c++ - HDF5:在数据集中插入一组字符串

    c++ - 使用 vector 调整大小时如何调用非默认构造函数?

    python - 是否有太多 python websocket 之类的事情?

    python - 通过 Web 应用程序处理作业 : real-time status updates and backend messaging

    PHP WebSocket ZMQ - 聊天操作 - 向特定用户发送数据