I have a simple setup of one client and one server. The client wants to execute a method in the server using ZeroMQ for communications. I am going to use the REQ and REP sockets because they are suitable for this use case. Nevertheless I have a doubt about the protobuf definitions. I think these two options can be used for achieving the goal:
message ControlService{
string control = 1;
int32 serverId = 2;
bool block = 3;
double temperature = 4;
}
Where "control" contains the name of the method to be executed remotely. The other alternative can be:
message InputParameters{
int32 serverId = 1;
bool block = 2;
double temperature = 3;
}
message Empty{
}
service ControlService{
rpc control (InputParameters) returns (Empty);
}
What would be the best approach? Or at least what are the trade-offs of using one approach instead of the other?
RPCZ is built on top of ZeroMQ for handling the low-level I/O details in a lock-free manner. The Python module is a Cython wrapper around the C++ API.
Protocol Buffer, a.k.a. Protobuf Protobuf is the most commonly used IDL (Interface Definition Language) for gRPC. It's where you basically store your data and function contracts in the form of a proto file.
gRPC uses a binary payload that is efficient to create and to parse, and it exploits HTTP/2 for efficient management of connections. Of course, you can also use binary payloads and HTTP/2 directly without using gRPC, but this requires you and your clients to master more technology.
Don't do it that way. Have a message:
message InputParameters{
req oneof
{
InputParametersA a = 1;
InputParametersB b = 2;
}
}
message InputParametersA
{
bool block = 1;
float temperature = 2;
}
message InputParametersB
{
<more fields>
}
That way you send only the InputParameters message. The method to call is dictated by whether InputParameters.req contains an InputParametersA (implies that method A should be called), or InputParmetersB (for method B).
This avoids parsing a string to determine a method name (highly error prone), and instead gives you an enumeration to switch on (the possible content of the req field). Depending on the implementation of GPB you're using (C++, etc) you may even get a compile time warning if your switch statement doesn't adequately cover all values of that enumeration.
It also means that there's no problems in determining which of the fields should be passed to a method; you're passing either InputParameters.req.a into method A or .b into method B. There's no need to break them out into separate parameters for a method, simple pass the whole thing in as a single parameter.
You can define different return types in the same way, passing them all back through a single oneof
.
Alternatives
Now if you were using ASN.1 (which is conceptually the same kind of thing as GPB), you could set constraints on the values and / or sizes of message fields (see here, Chapter 13 in this PDF. That way you'd have parameter validation being performed automatically, defined solely in the ASN.1 schema. The lack of value / size constraints in GPB is a glaring omission.
Take a look here (overview), here (free schema compiler for C/C++ that looks OK), and here (PDF, reference manual).
ASN.1 has stronger typing in its wire format (if you use BER encoding). It's possible to interrogate a wire bit stream to find out what type of message it contains. Thus there is no need to resort to wrapping all your possible messages up into a single oneof
like you do with GPB.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With