Hey everyone
I'm building an API with GRPC which currently looks like this:
serivce OurEndpoint {
rpc register (RegistrationForFeatureCeeAndDee) returns (stream FeatureCeeOrDeeRequest) {}
rpc featureA (FeatureAyeRequest) returns (FeatureAyeReponse) {}
rpc featureB (FeatureBeeRequest) returns (FeatureBeeResponse) {}
rpc offerFeatureC(FeatureCeeResponse) returns (Confirmation) {}
rpc offerFeatureD(FeatureDeeResponse) returns (Confirmation) {}
rpc offerCeeOrDeeFailed(FailureResponse) returns (Confirmation) {}
}
message FeatureCeeOrDeeRequest {
oneof request {
FeatureDeeRequest deeRequest = 1;
FeatureCeeRequest ceeRequest = 2;
}
}
message Confirmation {}
Note that features A and B are fairly traditional client-driven request-response pairs.
Features C and D are callbacks; the client registers with
I can provide answers to C and D, send me a message and I'll call offerFeatureResponse as appropriate.
I don't like this. It makes our application code complex. We effectively have to build our own multiplexer for things like offerCeeOrDeeFailed
What I'd really rather do is this:
serivce OurEndpoint {
rpc register (RegistrationForFeatureCeeAndDee) returns (Confirmation) {}
rpc featureA (FeatureAyeRequest) returns (FeatureAyeReponse) {}
rpc featureB (FeatureBeeRequest) returns (FeatureBeeResponse) {}
}
service EndpointClientMustImplement {
rpc featureC(FeatureCeeRequest) returns (FeatureCeeResponse) {}
rpc featureD(FeatureDeeRequest) returns (FeatureDeeResponse) {}
}
message RegistrationForFeatureCeeAndDee {
ConnectionToken name = 1;
}
message Confirmation {}
The problem here is how to go about implementing ConnectionToken and its handler. Ideally I'd like some code like this:
//kotlin, which is on the jvm.
override fun register(request: RegistrationForFeatureCeeAndDee, response: ResponseObserver<Confirmation>) {
//...
val channel: Channel = ManagedChannelBuilder
.for("localhost", 5551) // a port shared by the service handling this very response
.build()
val stub: EndpointClientMustImplement = EndpointClientMustImplement.newBuilder()
.withServiceNameOrSimilar(request.name)
.build()
//....
}
What is the best way to go about this?
1. Can I have multiple servers at a single address?
2. Whats the best way to find a service instance by name at runtime rather than by a type-derived (and thus by statically bound) name? I suspect the BindableService and ServerServiceDefinitions will help me here, but I really don't want to mess with the method-table building and the code generating system seems opaque.
I guess my idea solution would be to ask the code generator to generate code that is open on its service name, --ideally open on a constructor param such that there is no way to instance the service without specifying its service name.
Or, perhalps there's some other strategy I should be using? I could of course specify port numbers and then instance grpc services once-per-port, but that means I'm bounded on the number of ports I'm using by the number of active API users I have, which is very strange.
Many thanks!
-Geoff