grpc service definition style

87 views
Skip to first unread message

Jorg Heymans

unread,
Mar 9, 2017, 4:12:41 AM3/9/17
to grpc.io
Hi,

I am wondering what style people are using to define their grpc services. For example, a service def could be written something like this, in its most simple form:

syntax = "proto3";

package test;

message Ping {
  string theMessage = 1;
}

message Pong {
  string theResponse = 1;
}

service MyService {
  rpc SaySomething (Ping) returns (Pong);
}

Or, I have also seen this style where the payloads are encapsulated in separate request and response objects (more like how you would do it in schema perhaps)

syntax = "proto3";

package test;

message Ping {
  string theMessage = 1;
}

message PingRequest {
  Ping request = 1;
}

message Pong {
  string theResponse = 1;
}

message PongResponse {
  Pong response = 1;
}

service MyService {
  rpc SaySomething (PingRequest) returns (PongResponse);
}

I find that - at least in java - this definition generates a slightly easier to discover code syntax from the ide, but is it worthwhile to introduce the indirection and extra payload bytes just for this?

Would be nice to hear from long-time grpc users which style they prefer and why :-)

Thanks,
Jorg

Carl Mastrangelo

unread,
Mar 9, 2017, 2:07:51 PM3/9/17
to grpc.io
Not a long time user (though I used it's predecessor heavily).  Personally I like the first one.  IDE completion is not much of a problem for me, since I know that the interesting methods are either get<TAB> or set<Tab>.   Eclipse, for all its problems, does have pretty good autocompletion.  

I guess I don't see the benefit in the second approach.  Will PongResponse ever change?  If Pong is indeed the response, it should be the top level.  Also, including parts of both approaches would probably be best:

syntax = "proto3";

package test;

message PingRequest {
  
string theMessage = 1;
}

message PongResponse {
  
string theResponse = 1;
}

service MyService {
  rpc SaySomething (PingRequest) returns (PongResponse);
}

It is a common pattern to name the arguments to the method *Request and *Response.  

Also, I wouldn't worry about a few bytes of difference.  They will be lost in the noise.  TLS overhead will probably cost more in terms of size.  Same with HTTP/2 overhead, and gRPC framing overhead.  All are still pretty negligible. 

Michael Rose

unread,
Mar 9, 2017, 4:20:06 PM3/9/17
to grpc.io
For us, the decision comes down to having existing protocol buffers or not. Often times, we'll have a message defined for other purposes, e.x.:

message Contact {
  repeated
Name name = 1;
 
// etc..
}


And since we want RPC to be orthogonal from the storage models, we'll create wrappers:

message GetContactRequest {
 
string id = 1;
}


message
GetContactResponse {
 
Contact contact = 1;
 
// other metadata irrelevant to a "contact"
}

message
PutContactRequest {
 
string id = 1;
 
Contact contact = 2;
}

// etc.

Since we use protocol buffer throughout our stack (storage, task queueing, etc.), composing them into Request/Response wrappers make sense for us. If you're just using protocol buffers to drive logic that doesn't otherwise care about protocol buffers, it seems like the former is nicer.

Michael
Reply all
Reply to author
Forward
0 new messages