如何使用ZeroMQ为协议缓冲区编写自己的RPC实现
How can i write my own RPC Implementation for Protocol Buffers utilizing ZeroMQ
根据"定义服务"下的Google协议缓冲区文档,他们说
还可以将协议缓冲区与您自己的RPC实现一起使用。
据我所知,协议缓冲区并不是以本机方式实现RPC的。相反,它们提供了一系列必须由用户实现的抽象接口(这就是我!)。因此,我想利用ZeroMQ实现这些抽象接口用于网络通信。
我正试图使用ZeroMQ创建一个RPC实现,因为我正在进行的项目已经为基本消息传递实现了ZeroMQ(因此,正如文档所建议的,我而不是使用gRPC)。
在彻底阅读了proto文档后,我发现我必须实现抽象接口RpcChannel和RpcController才能实现自己的实现。
我已经用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-基于此
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,因为在我看到的示例中(请参阅此处的第三个代码块),它们传递一个具有要绑定的端口的字符串(即
MyRpcChannel channel("rpc:hostname:1234/myservice");
)
- 它似乎应该进入RPCChannel,因为在我看到的示例中(请参阅此处的第三个代码块),它们传递一个具有要绑定的端口的字符串(即
- 我关心我的RPCController实现,它似乎太简单了。应该有更多的人来这里吗
- 我如何实现RPCChannel,它似乎与SearchServiceImpl非常相似。这些类中的1虚拟函数有一个非常相似的方法签名,只是它是泛型的
以下是我遇到的其他一些堆栈溢出问题,其中包含了一些关于该主题的有用信息:
- Protobuf-Net:实现服务器、rpc控制器和rpc通道-这是我找到RPCController实现示例的地方
- 使用协议缓冲区在ZeroMQ中实现RPC-这个答案很有趣,因为在最重要的答案中,他们似乎建议不要为.proto文件使用RPC格式中内置的Protobufs。
- 我在这个文件中也注意到了同样的概念,在一个名为libpbrpc的存储库中,它似乎是一个很好的源代码,例如代码
- 我可以/应该使用现有的实现(如RPCZ)吗
感谢您的帮助。我希望我提供了足够的信息,并且清楚地知道我在寻找什么。如果有什么不清楚或缺乏信息,请告诉我。我很乐意相应地编辑这个问题。
- ZeroMQ为基于可以包含任何数据的消息的网络通信提供了低级API
- ProtoBuffers是一个将结构化数据编码为压缩二进制数据并对这些数据进行解码的库
- gRPC是一个RPC框架,它为基于网络通信的RPC服务生成代码,该服务具有将数据作为ProtoBuffers数据交换的功能
ZeroMQ和gRPC都以不同的方式提供对网络通信的支持。您必须选择ZeroMQ或gRPC进行网络通信。如果选择ZeroMQ,则可以使用交换二进制结构化数据的ProtoBuffer对消息进行编码。
重点是ProtoBuffers库允许对变体记录(类似于C/C++联合)进行编码和解码,可以完全模拟RPC服务提供的功能,RPC服务具有交换ProtoBuffer消息的功能。
所以选项是:
- 将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或基于gRPC的客户端
- 比较ZeroMQ与基于gRPC的服务的性能原因
- 特定功能,如在ZeroMQ与基于gRPC的服务和客户端中如何使用/处理订阅(请参阅如何在gRPC中正确设计发布订阅模式?)
对于第一个选项,与第二个选项相比,你必须做很多事情。您必须将发送的消息类型与预期接收的消息类型相匹配。
如果其他人将开发客户端,第二种选择将使您更容易/更快地了解所提供服务的功能。
为了在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++中为我自己的基于指针的数组分配内存的正确方法
- C++从对象自己的类中删除对象
- 使用 std::optional,而不是自己的结构
- 子轴围绕父轴而不是他自己的轴旋转
- 这个C++编译器优化(在自身的实例上调用对象自己的构造函数)的名称是什么,它是如何工作的?
- C++ 如何为自己的迭代器类从迭代器转换为const_iterator?
- 重载 + 自己的类和 std::string 的运算符
- 类无法访问自己的私有静态 constexpr 方法 - Clang bug?
- 是否可以在不填充自己的参数的情况下将模板函数作为参数传递?
- 如何访问模板参数自己的模板参数?
- 将矩阵乘以我自己的输入的向量
- 您应该在什么时候创建自己的异常类型
- 派生类是从基类继承 v 指针并仅使用它,还是也有自己的 v 指针?
- string1 == string2 和你自己的 for 循环比较有什么区别?
- 如何正确包含我自己的标头?
- 自己的自定义向量类. 内存重新分配
- 如何使用我构建的库,而不会从源代码出错,但不为我自己的项目编译?
- 如何使用ZeroMQ为协议缓冲区编写自己的RPC实现