core: add prepareToLoseNetwork() method to ManagedChannel

This commit is contained in:
Eric Gribkoff 2018-02-09 11:05:09 -08:00 committed by GitHub
parent 77397b9dd0
commit 6ee6eae5a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 79 additions and 14 deletions

View File

@ -119,6 +119,24 @@ public abstract class ManagedChannel extends Channel {
*
* @since 1.8.0
*/
@ExperimentalApi
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/4056")
public void resetConnectBackoff() {}
/**
* Invoking this method moves the channel into the IDLE state and triggers tear-down of the
* channel's name resolver and load balancer, while still allowing on-going RPCs on the channel to
* continue. New RPCs on the channel will trigger creation of a new connection.
*
* <p>This is primarily intended for Android users when a device is transitioning from a cellular
* to a wifi connection. Initially the device will maintain both the cellular and wifi
* connections, but the OS also issues a notification that after a short time the cellular
* connection will be terminated. Apps may invoke this method to ensure that new RPCs are created
* using the wifi connection, rather than the soon-to-be-disconnected cellular network.
*
* <p>No-op if not supported by implementation.
*
* @since 1.11.0
*/
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/4056")
public void prepareToLoseNetwork() {}
}

View File

@ -310,19 +310,7 @@ public final class ManagedChannelImpl extends ManagedChannel implements Instrume
// could cancel the timer.
return;
}
logger.log(Level.FINE, "[{0}] Entering idle mode", getLogId());
// nameResolver and loadBalancer are guaranteed to be non-null. If any of them were null,
// either the idleModeTimer ran twice without exiting the idle mode, or the task in shutdown()
// did not cancel idleModeTimer, both of which are bugs.
nameResolver.shutdown();
nameResolverStarted = false;
nameResolver = getNameResolver(target, nameResolverFactory, nameResolverParams);
lbHelper.lb.shutdown();
lbHelper = null;
subchannelPicker = null;
if (!channelStateManager.isDisabled()) {
channelStateManager.gotoState(IDLE);
}
enterIdleMode();
}
}
@ -368,6 +356,24 @@ public final class ManagedChannelImpl extends ManagedChannel implements Instrume
}
}
// Must be run from channelExecutor
private void enterIdleMode() {
logger.log(Level.FINE, "[{0}] Entering idle mode", getLogId());
// nameResolver and loadBalancer are guaranteed to be non-null. If any of them were null,
// either the idleModeTimer ran twice without exiting the idle mode, or the task in shutdown()
// did not cancel idleModeTimer, or prepareToLoseNetwork() ran while shutdown or in idle, all of
// which are bugs.
nameResolver.shutdown();
nameResolverStarted = false;
nameResolver = getNameResolver(target, nameResolverFactory, nameResolverParams);
lbHelper.lb.shutdown();
lbHelper = null;
subchannelPicker = null;
if (!channelStateManager.isDisabled()) {
channelStateManager.gotoState(IDLE);
}
}
// Must be run from channelExecutor
private void cancelIdleTimer() {
if (idleModeTimerFuture != null) {
@ -760,6 +766,22 @@ public final class ManagedChannelImpl extends ManagedChannel implements Instrume
}).drain();
}
@Override
public void prepareToLoseNetwork() {
class PrepareToLoseNetworkRunnable implements Runnable {
@Override
public void run() {
if (shutdown.get() || lbHelper == null) {
return;
}
cancelIdleTimer();
enterIdleMode();
}
}
channelExecutor.executeLater(new PrepareToLoseNetworkRunnable()).drain();
}
/**
* A registry that prevents channel shutdown from killing existing retry attempts that are in
* backoff.

View File

@ -1507,6 +1507,31 @@ public class ManagedChannelImplTest {
assertEquals(CONNECTING, channel.getState(false));
}
@Test
public void prepareToLoseNetworkEntersIdle() {
createChannel(new FakeNameResolverFactory(true), NO_INTERCEPTOR);
helper.updateBalancingState(READY, mockPicker);
assertEquals(READY, channel.getState(false));
channel.prepareToLoseNetwork();
assertEquals(IDLE, channel.getState(false));
}
@Test
public void prepareToLoseNetworkAfterIdleTimerIsNoOp() {
long idleTimeoutMillis = 2000L;
createChannel(
new FakeNameResolverFactory(true), NO_INTERCEPTOR, true /* request connection*/,
idleTimeoutMillis);
timer.forwardNanos(TimeUnit.MILLISECONDS.toNanos(idleTimeoutMillis));
assertEquals(IDLE, channel.getState(false));
channel.prepareToLoseNetwork();
assertEquals(IDLE, channel.getState(false));
}
@Test
public void updateBalancingStateDoesUpdatePicker() {
ClientStream mockStream = mock(ClientStream.class);