diff --git a/api/src/main/java/io/grpc/LoadBalancer.java b/api/src/main/java/io/grpc/LoadBalancer.java index 479ca4b2a83..b458580d6b1 100644 --- a/api/src/main/java/io/grpc/LoadBalancer.java +++ b/api/src/main/java/io/grpc/LoadBalancer.java @@ -362,6 +362,19 @@ public boolean canHandleEmptyAddressListFromNameResolution() { return false; } + /** + * The channel asks the LoadBalancer to establish connections now (if applicable) so that the + * upcoming RPC may then just pick a ready connection without waiting for connections. This + * is triggered by {@link ManagedChannel#getState ManagedChannel.getState(true)}. + * + *
If LoadBalancer doesn't override it, this is no-op. If it infeasible to create connections + * given the current state, e.g. no Subchannel has been created yet, LoadBalancer can ignore this + * request. + * + * @since 1.22.0 + */ + public void requestConnection() {} + /** * The main balancing logic. It must be thread-safe. Typically it should only * synchronize on its own state, and avoid synchronizing with the LoadBalancer's state. @@ -385,8 +398,10 @@ public abstract static class SubchannelPicker { * *
No-op if unsupported.
*
+ * @deprecated override {@link LoadBalancer#requestConnection} instead.
* @since 1.11.0
*/
+ @Deprecated
public void requestConnection() {}
}
diff --git a/core/src/main/java/io/grpc/internal/AutoConfiguredLoadBalancerFactory.java b/core/src/main/java/io/grpc/internal/AutoConfiguredLoadBalancerFactory.java
index cf006435aed..ada2ff6817e 100644
--- a/core/src/main/java/io/grpc/internal/AutoConfiguredLoadBalancerFactory.java
+++ b/core/src/main/java/io/grpc/internal/AutoConfiguredLoadBalancerFactory.java
@@ -175,6 +175,11 @@ public boolean canHandleEmptyAddressListFromNameResolution() {
return true;
}
+ @Override
+ public void requestConnection() {
+ getDelegate().requestConnection();
+ }
+
@Override
public void shutdown() {
delegate.shutdown();
diff --git a/core/src/main/java/io/grpc/internal/ManagedChannelImpl.java b/core/src/main/java/io/grpc/internal/ManagedChannelImpl.java
index ae7303697a5..bb8b283b09b 100644
--- a/core/src/main/java/io/grpc/internal/ManagedChannelImpl.java
+++ b/core/src/main/java/io/grpc/internal/ManagedChannelImpl.java
@@ -882,6 +882,7 @@ private void maybeTerminateChannel() {
}
@Override
+ @SuppressWarnings("deprecation")
public ConnectivityState getState(boolean requestConnection) {
ConnectivityState savedChannelState = channelStateManager.getState();
if (requestConnection && savedChannelState == IDLE) {
@@ -892,6 +893,9 @@ public void run() {
if (subchannelPicker != null) {
subchannelPicker.requestConnection();
}
+ if (lbHelper != null) {
+ lbHelper.lb.requestConnection();
+ }
}
}
diff --git a/core/src/main/java/io/grpc/internal/PickFirstLoadBalancer.java b/core/src/main/java/io/grpc/internal/PickFirstLoadBalancer.java
index 929c98a5576..5e8cd0c85b2 100644
--- a/core/src/main/java/io/grpc/internal/PickFirstLoadBalancer.java
+++ b/core/src/main/java/io/grpc/internal/PickFirstLoadBalancer.java
@@ -110,6 +110,13 @@ public void shutdown() {
}
}
+ @Override
+ public void requestConnection() {
+ if (subchannel != null) {
+ subchannel.requestConnection();
+ }
+ }
+
/**
* No-op picker which doesn't add any custom picking logic. It just passes already known result
* received in constructor.
@@ -140,10 +147,5 @@ public PickResult pickSubchannel(PickSubchannelArgs args) {
subchannel.requestConnection();
return PickResult.withNoResult();
}
-
- @Override
- public void requestConnection() {
- subchannel.requestConnection();
- }
}
}
diff --git a/core/src/main/java/io/grpc/util/ForwardingLoadBalancer.java b/core/src/main/java/io/grpc/util/ForwardingLoadBalancer.java
index a5c086fe131..4f1b4407171 100644
--- a/core/src/main/java/io/grpc/util/ForwardingLoadBalancer.java
+++ b/core/src/main/java/io/grpc/util/ForwardingLoadBalancer.java
@@ -67,6 +67,11 @@ public boolean canHandleEmptyAddressListFromNameResolution() {
return delegate().canHandleEmptyAddressListFromNameResolution();
}
+ @Override
+ public void requestConnection() {
+ delegate().requestConnection();
+ }
+
@Override
public String toString() {
return MoreObjects.toStringHelper(this).add("delegate", delegate()).toString();
diff --git a/core/src/test/java/io/grpc/internal/ManagedChannelImplTest.java b/core/src/test/java/io/grpc/internal/ManagedChannelImplTest.java
index 60c13d0b2c9..336714a6ac5 100644
--- a/core/src/test/java/io/grpc/internal/ManagedChannelImplTest.java
+++ b/core/src/test/java/io/grpc/internal/ManagedChannelImplTest.java
@@ -1920,6 +1920,7 @@ public void getState_withRequestConnect() {
verify(mockLoadBalancerProvider).newLoadBalancer(any(Helper.class));
}
+ @SuppressWarnings("deprecation")
@Test
public void getState_withRequestConnect_IdleWithLbRunning() {
channelBuilder.nameResolverFactory(
@@ -1932,6 +1933,7 @@ public void getState_withRequestConnect_IdleWithLbRunning() {
assertEquals(IDLE, channel.getState(true));
verify(mockLoadBalancerProvider).newLoadBalancer(any(Helper.class));
verify(mockPicker).requestConnection();
+ verify(mockLoadBalancer).requestConnection();
}
@Test
diff --git a/core/src/test/java/io/grpc/internal/PickFirstLoadBalancerTest.java b/core/src/test/java/io/grpc/internal/PickFirstLoadBalancerTest.java
index c853cdb11f4..2efee39bcaf 100644
--- a/core/src/test/java/io/grpc/internal/PickFirstLoadBalancerTest.java
+++ b/core/src/test/java/io/grpc/internal/PickFirstLoadBalancerTest.java
@@ -262,14 +262,18 @@ public void nameResolutionErrorWithStateChanges() throws Exception {
@Test
public void requestConnection() {
+ loadBalancer.requestConnection();
+ verify(mockSubchannel, never()).requestConnection();
+
loadBalancer.handleResolvedAddresses(
ResolvedAddresses.newBuilder().setAddresses(servers).setAttributes(affinity).build());
+ verify(mockSubchannel).requestConnection();
+
loadBalancer.handleSubchannelState(mockSubchannel, ConnectivityStateInfo.forNonError(IDLE));
- verify(mockHelper).updateBalancingState(eq(IDLE), pickerCaptor.capture());
- SubchannelPicker picker = pickerCaptor.getValue();
+ verify(mockHelper).updateBalancingState(eq(IDLE), any(SubchannelPicker.class));
verify(mockSubchannel).requestConnection();
- picker.requestConnection();
+ loadBalancer.requestConnection();
verify(mockSubchannel, times(2)).requestConnection();
}
diff --git a/grpclb/src/main/java/io/grpc/grpclb/GrpclbLoadBalancer.java b/grpclb/src/main/java/io/grpc/grpclb/GrpclbLoadBalancer.java
index 88dd4378dc4..13ccb19e824 100644
--- a/grpclb/src/main/java/io/grpc/grpclb/GrpclbLoadBalancer.java
+++ b/grpclb/src/main/java/io/grpc/grpclb/GrpclbLoadBalancer.java
@@ -115,6 +115,13 @@ public void handleResolvedAddresses(ResolvedAddresses resolvedAddresses) {
grpclbState.handleAddresses(newLbAddressGroups, newBackendServers);
}
+ @Override
+ public void requestConnection() {
+ if (grpclbState != null) {
+ grpclbState.requestConnection();
+ }
+ }
+
@VisibleForTesting
static Mode retrieveModeFromLbConfig(
@Nullable Map