grpc / grpc-java

The Java gRPC implementation. HTTP/2 based RPC
https://grpc.io/docs/languages/java/
Apache License 2.0
11.38k stars 3.83k forks source link

Allow turning `BindableService` into a `ServerTransportListener`-like structure #11546

Open bsideup opened 1 week ago

bsideup commented 1 week ago

Is your feature request related to a problem?

Currently, there is no easy way to trigger a gRPC service from an existing HTTP/2 handler.

Describe the solution you'd like

Expose a way to call a service by calling it with a raw HTTP/2 request. Something like:

ServerBuilder.forFactory(ClientTransportServersBuilder)

Describe alternatives you've considered

A custom server can be implemented, but it requires many steps and operates on low level abstractions (e.g. Netty channels, or Sockets), whereas most modern frameworks have their own HTTP/2 abstractions that can be turned into a ServerStream.

Additional context

https://github.com/bsideup/grpc-bidi uses an existing ClientCall and exposes itself (client) as a server. The current implementation requires going through the full pipeline, but all is does is parses the bytes from ClientCall without any network stack, and it could use a lean implementation where it simply parses the HTTP/2 request and turns it into a ServerStream.

bsideup commented 1 week ago

Okay, now I feel kinda silly because it can be achieved with new ServerImplBuilder(streamTracerFactories -> new ChannelServer(streamTracerFactories, networkChannel)).

It does require the use of an internal class but for my use case it is maybe fine? Will leave it open for the discussion tho.

ejona86 commented 1 week ago

No, it's not fine to use ServerImpl. That's clearly internal and will break on our whim.

I really don't understand what you're trying to do. But based on "exposes itself (client) as a server" sounds like a good use of InProcess transport. In-process has pretty low overhead, and with directExecutor() (which only makes sense for certain use cases) it is ~2-3 µs (based on some old benchmarks I see) for a noop RPC.

A way to call gRPC with an existing request is with the grpc-servlet API. But then you'd need to represent the "existing HTTP/2 request" in terms of the servlet API.

I see you are doing some sort of tunneling. If you haven't seen it, see if https://github.com/grpc/grpc-java/pull/3987 is useful at all to you.

bsideup commented 1 week ago

Hi @ejona86! Funny that you mentioned your PR because I've previously submitted https://github.com/grpc/grpc-java/pull/11023 as a continuation of it, and grpc-bidi is basically the end result, extracted so it can be consumed already now (it's been in prod for 6+ months), but I hope to land it in https://github.com/grpc-ecosystem and eventually grpc-java if it gets accepted to the spec :)

Cool idea to look at grpc-servlet, thank you! I will investigate. I think I looked into in-process in the past but there were some issues with it, I could be wrong tho. Let me double check! Thanks for the pointers 👍

ejona86 commented 1 week ago

But then you'd need to represent the "existing HTTP/2 request" in terms of the servlet API.

Beware that is doing some heavy lifting. In particular, we use the async APIs. It's more, "that's the only general-purpose HTTP API we support."