grpc / grpc-java

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

ConnectivityStateManager Status notification problem #11615

Closed hlx502 closed 1 week ago

hlx502 commented 1 month ago
  1. Why does notifyWhenStateChanged() execute callback immediately when state != source? image
  2. Do we need to clear listeners for each state change? image
ejona86 commented 1 month ago

From the Javadoc:

Registers a one-off callback that will be run if the connectivity state of the channel diverges from the given source, which is typically what has just been returned by getState(boolean). If the states are already different, the callback will be called immediately.

What do you hope the passed source is used for? source is what you think the current state is, and the callback is called when the channel is no longer that state. The API is inherently (and purposefully) racy, and accounts for that race by immediately notifying if source is out-of-date.

hlx502 commented 1 month ago

Understood. Our client uses a domain name to connect to the server, but the server IP may change, such as in the k8s environment. When reconnecting through the automatic reconnection mechanism, the IP cannot be resolved based on the domain name. We will manually rebuild the channel based on its status, but it seems that notifyWhenStateChanged cannot be used. Do you have any suggestions on your end? Thanks

ejona86 commented 1 month ago

the IP cannot be resolved based on the domain name

Why not? The client will re-resolve DNS when a connection is dropped/failed. So it will do the same as you're wanting to do.

If there's only one IP, then a regular k8s service should be fine; you wouldn't benefit from a headless service.

but it seems that notifyWhenStateChanged cannot be used

You can learn about TRANSIENT_FAILURE...

private void handleStateChange() {
  ConnectivityState state = channel.getState(false);
  if (state == TRANSIENT_FAILURE) {
    // react
  } else {
    channel.notifyWhenStateChanged(state, this::handleStateChange);
  }
}

channel.notifyWhenStateChanged(ConnectivityState.IDLE, this::handleStateChange);

Not that I'd encourage doing that to tear down the channel. If you do that, at the very least double-check that the address has actually changed before throwing away the TRANSIENT_FAILURE channel.

ejona86 commented 1 week ago

No response, and seems like this could be resolved. If not, comment, and it can be reopened.