The reason why gRPC streaming services can only have a single parameter
Asked Answered
E

1

7

At the moment, gRPC service definitions can only have a single parameter even if it’s a streaming service which makes challenging to represent the “initial” request. For instance, consider a chat app in which a user can join a chat room.

In this case, the domain can be modelled as follows.

message JoinRoomRequest {
  required string room = 1;
}

message ChatMessage {
  required string content = 2;
}

A consumer of the chat app would send a join request and start a bi-directional stream of messages, so the service can be described in this way.

service SimpleChat {
  rpc joinChatRoom (JoinRoomRequest, stream ChatMessage) returns (stream ChatMessage);
}

However, in gRPC the above syntax is not valid. The only way to represent the described chat service is

service SimpleChat {
  rpc joinChatRoom (stream ChatMessage) returns (stream ChatMessage);
}

What is the reason behind this decision, and how a similar domain can be modelled in gRPC?

Egghead answered 28/7, 2019 at 6:58 Comment(0)
C
4

Simplicity. It is much easier to model a request/response as a single payload rather than varadic, especially in your case, where you want the multiplicity to be different - what, for example, would it mean to take

(A, stream B, C, stream D)

And... if you can take multiple elements, can you return multiple too? Many languages support that concept, after all.

No, it is much easier to receive (as either input it output) either a request or a stream of requests of a single type.

In your scenario, perhaps consider the request type being a thing wrapper over a oneof (discriminated union) of all the actual expected messages, and just have your code enforce that the first one is a "join":

message ChatRequest {
  oneof RequestType {
    JoinRoomRequest join = 1;
    ChatMessage message = 2;
  }
}

And take a stream of ChatRequest

Cherimoya answered 28/7, 2019 at 9:44 Comment(2)
Thanks! Do you have a source for the answer by any chance? I definitely understand the argument for simplicity, however protocol-wise, having a message as a part of the initial payload maps to (A, stream<B> => [C], stream<D>) perfectly, and all of the domains I've tried to model require "A". Such interaction is supported by e.g. rsocket. So, I'd be keen on hearing the "official" opinion if anyone happened to be involved in any of the related discussions.Egghead
@serce it isn't stated explicitly as such in the documentation as far as I can see, but in practice: that's how it is defined in gRPC. The best citation I can give is MethodDescriptorProto in descriptor.proto, which clearly defines a single input and output type: github.com/protocolbuffers/protobuf/blob/master/src/google/…Cherimoya

© 2022 - 2024 — McMap. All rights reserved.