I see both Netty and OkHttp implementations in GRPC automatically acknowledge pings. But they don't expose a way to issue them. I think this would be valuable for interesting/advanced connection management. Ideally, the ClientTransport would expose a method that would also be accessible via the ChannelImpl:ListenableFuture<Void> ping();
I have a branch where I've started adding this. I'm curious what folks think about it. I actually have some other local changes that add even more to ChannelImpl, to expose things for additional connection management usages:// Currently, transport is started lazily as needed from// newCall(...). This allows the transport to be started// eagerly to avoid connection delays.void tryStartTransport();
// Stops active transport (if there is one) and starts a// new one. This can be useful if the endpoint is a load// balancer and you want to get a persistent connection to// a different backend behind the load balancer.void recreateTransport();
// Listeners, for knowing when a channel is connected or not.boolean addListener(Listener);boolean removeListener(Listener);interface Listener {// transport startedchannelTransportStarted(ClientTransport transport);// transport is ready (e.g. connection established)// also requires a new method on ClientTransport.ListenerchannelTransportReady(ClientTransport transport);// transport is shutting downchannelTransportShutdown(ClientTransport transport);}
On Thu, May 7, 2015 at 8:32 PM, Josh Humphries <j...@squareup.com> wrote:I see both Netty and OkHttp implementations in GRPC automatically acknowledge pings. But they don't expose a way to issue them. I think this would be valuable for interesting/advanced connection management. Ideally, the ClientTransport would expose a method that would also be accessible via the ChannelImpl:ListenableFuture<Void> ping();This idea sounds good, and doing it at the transport layer is appropriate.We have been avoiding ListenableFuture as of late in APIs in order permit reducing dependency on Guava in the future. Since this is in the transport, which we plan to give much weaker API guarantees for, that can be fine though.
We may want the API to provide the actual RTT discovered from the ping.
I have a branch where I've started adding this. I'm curious what folks think about it. I actually have some other local changes that add even more to ChannelImpl, to expose things for additional connection management usages:// Currently, transport is started lazily as needed from// newCall(...). This allows the transport to be started// eagerly to avoid connection delays.void tryStartTransport();We'd like Channel to expose higher-level APIs, not ones for low-level connection management. We know we will need user-custom load balancing, so we will already be wanting to figure out how to plumb alternative channels with advanced details or plugging in alternative algorithms. We could have these various methods you are making protected, for instance, but that still leaves problems with constructing with ChannelBuilder.For the "normal-user" experience here, I was thinking more along the terms of an option/setting that enabled eager (instead of lazy) connecting. It seems many users who would want the transport to start connecting now would also want it to reconnect semi-immediately if it gets disconnected. Alternatively, there could be a "waitForConnected" call that implicitly starts connecting (and a timeout of 0 would start connecting, but not block).
// Stops active transport (if there is one) and starts a// new one. This can be useful if the endpoint is a load// balancer and you want to get a persistent connection to// a different backend behind the load balancer.void recreateTransport();
// Listeners, for knowing when a channel is connected or not.boolean addListener(Listener);boolean removeListener(Listener);interface Listener {// transport startedchannelTransportStarted(ClientTransport transport);// transport is ready (e.g. connection established)// also requires a new method on ClientTransport.ListenerchannelTransportReady(ClientTransport transport);// transport is shutting downchannelTransportShutdown(ClientTransport transport);}The high-level version of this sort of stuff was going to be discussed in https://github.com/grpc/grpc-java/issues/28 . Importantly, it wouldn't want it to expose transports, as we want those to stay within Channel.We were thinking of just exposing what state we were in: idle, connecting, ready, failure. Idle means "no active transport." Connecting means "active transport but it doesn't work yet." Ready means "working active transport." And failure means "transport failed; we may wait until trying to connect again". This was being discussed in an internal doc; I'd start working on putting it on GitHub, but it is out-of-date :'(. I'll try to push things in the right direction.
If the high-level APIs won't do what you need, then creating low level Channel interaction sounds good, although I don't know what form that should look like.
On Fri, May 8, 2015 at 1:15 PM, Eric Anderson <ej...@google.com> wrote:On Thu, May 7, 2015 at 8:32 PM, Josh Humphries <j...@squareup.com> wrote:We have been avoiding ListenableFuture as of late in APIs in order permit reducing dependency on Guava in the future. Since this is in the transport, which we plan to give much weaker API guarantees for, that can be fine though.So maybe having it accept a Runnable (callback) is better than adding dep on Guava.
BTW, I have most of this already implemented in a branch. Should I try to clean it up and send a pull request?
Or is this work that someone else is already doing, related to the health-checking API stuff you mentioned?
We may want the API to provide the actual RTT discovered from the ping.Maybe, but this is quite easy for the caller to measure. Maybe the callback could accept the duration instead of just being a Runnable.
Yeah, that was how I started when I was toying with this. But even an "always connected" strategy will likely need some customization -- like how long to delay between connection attempts (including support for exponential back-off), how long to optionally block for the transport to become connected/ready when trying to send a request, and how to auto-reply to the call if the channel never became available (e.g. immediate Status.UNAVAILABLE).
Trying to make it flexible and pluggable while also keeping a sane and simple API was challenging. So I pivoted with the thought that all of this could be layered on top (like via ClientInterceptor) if we just exposed a few simple low-level primitives like these.
I definitely like the idea of exposing a state and not exposing the underlying transport. But I still think the listener is valuable (just altered to emit state change events) so that things can promptly react to state changes without polling. The set of states you list sounds perfect.
If the high-level APIs won't do what you need, then creating low level Channel interaction sounds good, although I don't know what form that should look like.I have a branch where I've made all of these changes, but it sounds like I might be stepping on toes or pushing in a different direction than you already had in mind. Let's continue to discuss, and maybe I can get that branch into the right shape and open a pull request?
I'll have to look into it. Animal sniffer always gave clear messages when it failed in the past. We use animal sniffer to ensure we maintain Java 6 compatibility for Android.
-PskipCodegen=true was added this week. Check out the updated README.md that describes it.
The (currently internal) doc is opinionated about interesting details of the exact look of the listener, it's on my plate to may it public. I'd be sooo happy if I get it done today.
The docs I had in mind are now public, but not yet committed: