From 0161c8a18250d5e70067180a38efa64d1a1506c8 Mon Sep 17 00:00:00 2001 From: Terry Wilson Date: Wed, 15 Mar 2023 11:33:33 -0700 Subject: [PATCH 1/5] examples: custom load balancer example --- examples/BUILD.bazel | 9 + examples/build.gradle | 8 + .../CustomLoadBalanceClient.java | 109 ++++++++++ .../ShufflingPickFirstLoadBalancer.java | 205 ++++++++++++++++++ ...hufflingPickFirstLoadBalancerProvider.java | 42 ++++ 5 files changed, 373 insertions(+) create mode 100644 examples/src/main/java/io/grpc/examples/customloadbalance/CustomLoadBalanceClient.java create mode 100644 examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancer.java create mode 100644 examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancerProvider.java diff --git a/examples/BUILD.bazel b/examples/BUILD.bazel index 2a5ed52b35c..cd810eb4892 100644 --- a/examples/BUILD.bazel +++ b/examples/BUILD.bazel @@ -154,6 +154,15 @@ java_binary( ], ) +java_binary( + name = "custom-load-balance-client", + testonly = 1, + main_class = "io.grpc.examples.customloadbalance.CustomLoadBalanceClient", + runtime_deps = [ + ":examples", + ], +) + java_binary( name = "name-resolve-client", testonly = 1, diff --git a/examples/build.gradle b/examples/build.gradle index c45e8e9da54..2208f3c0de4 100644 --- a/examples/build.gradle +++ b/examples/build.gradle @@ -153,6 +153,13 @@ task loadBalanceClient(type: CreateStartScripts) { classpath = startScripts.classpath } +task customLoadBalanceClient(type: CreateStartScripts) { + mainClass = 'io.grpc.examples.customloadbalance.CustomLoadBalanceClient' + applicationName = 'custom-load-balance-client' + outputDir = new File(project.buildDir, 'tmp/scripts/' + name) + classpath = startScripts.classpath +} + task nameResolveServer(type: CreateStartScripts) { mainClass = 'io.grpc.examples.nameresolve.NameResolveServer' applicationName = 'name-resolve-server' @@ -181,6 +188,7 @@ applicationDistribution.into('bin') { from(manualFlowControlServer) from(loadBalanceServer) from(loadBalanceClient) + from(customLoadBalanceClient) from(nameResolveServer) from(nameResolveClient) fileMode = 0755 diff --git a/examples/src/main/java/io/grpc/examples/customloadbalance/CustomLoadBalanceClient.java b/examples/src/main/java/io/grpc/examples/customloadbalance/CustomLoadBalanceClient.java new file mode 100644 index 00000000000..c66ba14239c --- /dev/null +++ b/examples/src/main/java/io/grpc/examples/customloadbalance/CustomLoadBalanceClient.java @@ -0,0 +1,109 @@ +/* + * Copyright 2022 The gRPC Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.grpc.examples.customloadbalance; + +import com.google.gson.Gson; +import io.grpc.Channel; +import io.grpc.LoadBalancerRegistry; +import io.grpc.ManagedChannel; +import io.grpc.ManagedChannelBuilder; +import io.grpc.NameResolverRegistry; +import io.grpc.StatusRuntimeException; +import io.grpc.examples.helloworld.GreeterGrpc; +import io.grpc.examples.helloworld.HelloReply; +import io.grpc.examples.helloworld.HelloRequest; +import io.grpc.examples.loadbalance.ExampleNameResolverProvider; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * This client is intended for connecting with the @{link LoadBalanceServer} in the "loadbalance" + * example. + */ +public class CustomLoadBalanceClient { + + private static final Logger logger = Logger.getLogger(CustomLoadBalanceClient.class.getName()); + + public static final String exampleScheme = "example"; + public static final String exampleServiceName = "lb.example.grpc.io"; + + private final GreeterGrpc.GreeterBlockingStub blockingStub; + + public CustomLoadBalanceClient(Channel channel) { + blockingStub = GreeterGrpc.newBlockingStub(channel); + } + + public void greet(String name) { + HelloRequest request = HelloRequest.newBuilder().setName(name).build(); + HelloReply response; + try { + response = blockingStub.sayHello(request); + } catch (StatusRuntimeException e) { + logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus()); + return; + } + logger.info("Greeting: " + response.getMessage()); + } + + + public static void main(String[] args) throws Exception { + // We need to register the provider of our custom load balancer implementation + LoadBalancerRegistry.getDefaultRegistry() + .register(new ShufflingPickFirstLoadBalancerProvider()); + + NameResolverRegistry.getDefaultRegistry().register(new ExampleNameResolverProvider()); + + String target = String.format("%s:///%s", exampleScheme, exampleServiceName); + + logger.info("Use default first_pick load balance policy"); + ManagedChannel channel = ManagedChannelBuilder.forTarget(target) + .usePlaintext() + .build(); + try { + CustomLoadBalanceClient client = new CustomLoadBalanceClient(channel); + for (int i = 0; i < 5; i++) { + client.greet("request" + i); + } + } finally { + channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS); + } + + logger.info("Change to custom shuffling_pick_first policy with a configured random seed"); + // The load balancer name in the config needs to match what getPolicyName() in the provider + // returns. The randomSeed field we are using also needs to be understood by the provider when + // parseLoadBalancingPolicyConfig() gets called. + Map serviceConfig = new Gson().fromJson( + "{ \"loadBalancingConfig\": " + + " [ { \"example.shuffling_pick_first\": { \"randomSeed\": 123 } } ]" + + "}", + Map.class); + channel = ManagedChannelBuilder.forTarget(target) + .defaultServiceConfig(serviceConfig) + .usePlaintext() + .build(); + try { + CustomLoadBalanceClient client = new CustomLoadBalanceClient(channel); + for (int i = 0; i < 5; i++) { + client.greet("request" + i); + } + } finally { + channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS); + } + } +} diff --git a/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancer.java b/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancer.java new file mode 100644 index 00000000000..ce1d10bbb24 --- /dev/null +++ b/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancer.java @@ -0,0 +1,205 @@ +/* + * Copyright 2023 The gRPC Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.grpc.examples.customloadbalance; + +import static com.google.common.base.Preconditions.checkNotNull; +import static io.grpc.ConnectivityState.CONNECTING; +import static io.grpc.ConnectivityState.IDLE; +import static io.grpc.ConnectivityState.SHUTDOWN; +import static io.grpc.ConnectivityState.TRANSIENT_FAILURE; + +import com.google.common.base.MoreObjects; +import io.grpc.ConnectivityState; +import io.grpc.ConnectivityStateInfo; +import io.grpc.EquivalentAddressGroup; +import io.grpc.LoadBalancer; +import io.grpc.Status; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * An example {@link LoadBalancer} largely based on {@link ShufflingPickFirstLoadBalancer} that adds + * shuffling of the list of servers so that the first server provided in {@link ResolvedAddresses} + * won't necessarily be the server the channel will connect to. + */ +public class ShufflingPickFirstLoadBalancer extends LoadBalancer { + + private final Helper helper; + private Subchannel subchannel; + + /** + * This class defines the configuration used by this {@link LoadBalancer}. Note that no part of + * the gRPC library is aware of this interface, it will be populated by the + * {@link ShufflingPickFirstLoadBalancerProvider}. + */ + static class ShufflingPickFirstLoadBalancerConfig { + + final Long randomSeed; + + ShufflingPickFirstLoadBalancerConfig(Long randomSeed) { + this.randomSeed = randomSeed; + } + } + + public ShufflingPickFirstLoadBalancer(Helper helper) { + this.helper = checkNotNull(helper, "helper"); + } + + @Override + public boolean acceptResolvedAddresses(ResolvedAddresses resolvedAddresses) { + List servers = new ArrayList<>(resolvedAddresses.getAddresses()); + if (servers.isEmpty()) { + handleNameResolutionError(Status.UNAVAILABLE.withDescription( + "NameResolver returned no usable address. addrs=" + resolvedAddresses.getAddresses() + + ", attrs=" + resolvedAddresses.getAttributes())); + return false; + } + + ShufflingPickFirstLoadBalancerConfig config + = (ShufflingPickFirstLoadBalancerConfig) resolvedAddresses.getLoadBalancingPolicyConfig(); + + Collections.shuffle(servers, + config.randomSeed != null ? new Random(config.randomSeed) : new Random()); + + if (subchannel == null) { + final Subchannel subchannel = helper.createSubchannel( + CreateSubchannelArgs.newBuilder() + .setAddresses(servers) + .build()); + subchannel.start(new SubchannelStateListener() { + @Override + public void onSubchannelState(ConnectivityStateInfo stateInfo) { + processSubchannelState(subchannel, stateInfo); + } + }); + this.subchannel = subchannel; + + helper.updateBalancingState(CONNECTING, + new Picker(PickResult.withSubchannel(this.subchannel))); + subchannel.requestConnection(); + } else { + subchannel.updateAddresses(servers); + } + + return true; + } + + @Override + public void handleNameResolutionError(Status error) { + if (subchannel != null) { + subchannel.shutdown(); + subchannel = null; + } + helper.updateBalancingState(TRANSIENT_FAILURE, new Picker(PickResult.withError(error))); + } + + private void processSubchannelState(Subchannel subchannel, ConnectivityStateInfo stateInfo) { + ConnectivityState currentState = stateInfo.getState(); + if (currentState == SHUTDOWN) { + return; + } + if (stateInfo.getState() == TRANSIENT_FAILURE || stateInfo.getState() == IDLE) { + helper.refreshNameResolution(); + } + + SubchannelPicker picker; + switch (currentState) { + case IDLE: + picker = new RequestConnectionPicker(subchannel); + break; + case CONNECTING: + picker = new Picker(PickResult.withNoResult()); + break; + case READY: + picker = new Picker(PickResult.withSubchannel(subchannel)); + break; + case TRANSIENT_FAILURE: + picker = new Picker(PickResult.withError(stateInfo.getStatus())); + break; + default: + throw new IllegalArgumentException("Unsupported state:" + currentState); + } + helper.updateBalancingState(currentState, picker); + } + + + @Override + public void shutdown() { + if (subchannel != null) { + subchannel.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. + */ + private static final class Picker extends SubchannelPicker { + + private final PickResult result; + + Picker(PickResult result) { + this.result = checkNotNull(result, "result"); + } + + @Override + public PickResult pickSubchannel(PickSubchannelArgs args) { + return result; + } + + @Override + public String toString() { + return MoreObjects.toStringHelper(Picker.class).add("result", result).toString(); + } + } + + /** + * Picker that requests connection during the first pick, and returns noResult. + */ + private final class RequestConnectionPicker extends SubchannelPicker { + + private final Subchannel subchannel; + private final AtomicBoolean connectionRequested = new AtomicBoolean(false); + + RequestConnectionPicker(Subchannel subchannel) { + this.subchannel = checkNotNull(subchannel, "subchannel"); + } + + @Override + public PickResult pickSubchannel(PickSubchannelArgs args) { + if (connectionRequested.compareAndSet(false, true)) { + helper.getSynchronizationContext().execute(new Runnable() { + @Override + public void run() { + subchannel.requestConnection(); + } + }); + } + return PickResult.withNoResult(); + } + } +} \ No newline at end of file diff --git a/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancerProvider.java b/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancerProvider.java new file mode 100644 index 00000000000..cb965d839f5 --- /dev/null +++ b/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancerProvider.java @@ -0,0 +1,42 @@ +package io.grpc.examples.customloadbalance; + +import io.grpc.LoadBalancer; +import io.grpc.LoadBalancer.Helper; +import io.grpc.LoadBalancerProvider; +import io.grpc.NameResolver.ConfigOrError; +import io.grpc.examples.customloadbalance.ShufflingPickFirstLoadBalancer.ShufflingPickFirstLoadBalancerConfig; +import java.util.Map; + +public class ShufflingPickFirstLoadBalancerProvider extends LoadBalancerProvider { + + private static final String RANDOM_SEED_KEY = "randomSeed"; + + @Override + public ConfigOrError parseLoadBalancingPolicyConfig(Map rawLoadBalancingPolicyConfig) { + Long randomSeed = null; + if (rawLoadBalancingPolicyConfig.containsKey(RANDOM_SEED_KEY)) { + randomSeed = ((Double) rawLoadBalancingPolicyConfig.get(RANDOM_SEED_KEY)).longValue(); + } + return ConfigOrError.fromConfig(new ShufflingPickFirstLoadBalancerConfig(randomSeed)); + } + + @Override + public LoadBalancer newLoadBalancer(Helper helper) { + return new ShufflingPickFirstLoadBalancer(helper); + } + + @Override + public boolean isAvailable() { + return true; + } + + @Override + public int getPriority() { + return 5; + } + + @Override + public String getPolicyName() { + return "example.shuffling_pick_first"; + } +} From 4a08d06710c7fdeb2bfc267158446dce6109bf73 Mon Sep 17 00:00:00 2001 From: Terry Wilson Date: Fri, 17 Mar 2023 14:44:32 -0700 Subject: [PATCH 2/5] Review feedback --- .../CustomLoadBalanceClient.java | 8 +++-- .../ShufflingPickFirstLoadBalancer.java | 8 ++--- ...hufflingPickFirstLoadBalancerProvider.java | 29 +++++++++++++++++-- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/examples/src/main/java/io/grpc/examples/customloadbalance/CustomLoadBalanceClient.java b/examples/src/main/java/io/grpc/examples/customloadbalance/CustomLoadBalanceClient.java index c66ba14239c..6c6481d2795 100644 --- a/examples/src/main/java/io/grpc/examples/customloadbalance/CustomLoadBalanceClient.java +++ b/examples/src/main/java/io/grpc/examples/customloadbalance/CustomLoadBalanceClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2022 The gRPC Authors + * Copyright 2023 The gRPC Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,8 @@ import com.google.gson.Gson; import io.grpc.Channel; +import io.grpc.Grpc; +import io.grpc.InsecureChannelCredentials; import io.grpc.LoadBalancerRegistry; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; @@ -72,9 +74,9 @@ public static void main(String[] args) throws Exception { String target = String.format("%s:///%s", exampleScheme, exampleServiceName); logger.info("Use default first_pick load balance policy"); - ManagedChannel channel = ManagedChannelBuilder.forTarget(target) - .usePlaintext() + ManagedChannel channel = Grpc.newChannelBuilder(target, InsecureChannelCredentials.create()) .build(); + try { CustomLoadBalanceClient client = new CustomLoadBalanceClient(channel); for (int i = 0; i < 5; i++) { diff --git a/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancer.java b/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancer.java index ce1d10bbb24..01e720f0cb6 100644 --- a/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancer.java +++ b/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancer.java @@ -49,11 +49,11 @@ public class ShufflingPickFirstLoadBalancer extends LoadBalancer { * the gRPC library is aware of this interface, it will be populated by the * {@link ShufflingPickFirstLoadBalancerProvider}. */ - static class ShufflingPickFirstLoadBalancerConfig { + static class Config { final Long randomSeed; - ShufflingPickFirstLoadBalancerConfig(Long randomSeed) { + Config(Long randomSeed) { this.randomSeed = randomSeed; } } @@ -72,8 +72,8 @@ public boolean acceptResolvedAddresses(ResolvedAddresses resolvedAddresses) { return false; } - ShufflingPickFirstLoadBalancerConfig config - = (ShufflingPickFirstLoadBalancerConfig) resolvedAddresses.getLoadBalancingPolicyConfig(); + Config config + = (Config) resolvedAddresses.getLoadBalancingPolicyConfig(); Collections.shuffle(servers, config.randomSeed != null ? new Random(config.randomSeed) : new Random()); diff --git a/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancerProvider.java b/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancerProvider.java index cb965d839f5..a1381186b28 100644 --- a/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancerProvider.java +++ b/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancerProvider.java @@ -1,10 +1,26 @@ +/* + * Copyright 2023 The gRPC Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package io.grpc.examples.customloadbalance; import io.grpc.LoadBalancer; import io.grpc.LoadBalancer.Helper; import io.grpc.LoadBalancerProvider; import io.grpc.NameResolver.ConfigOrError; -import io.grpc.examples.customloadbalance.ShufflingPickFirstLoadBalancer.ShufflingPickFirstLoadBalancerConfig; +import io.grpc.Status; import java.util.Map; public class ShufflingPickFirstLoadBalancerProvider extends LoadBalancerProvider { @@ -15,9 +31,16 @@ public class ShufflingPickFirstLoadBalancerProvider extends LoadBalancerProvider public ConfigOrError parseLoadBalancingPolicyConfig(Map rawLoadBalancingPolicyConfig) { Long randomSeed = null; if (rawLoadBalancingPolicyConfig.containsKey(RANDOM_SEED_KEY)) { - randomSeed = ((Double) rawLoadBalancingPolicyConfig.get(RANDOM_SEED_KEY)).longValue(); + // The load balancing configuration generally comes from a remote source over the wire, be + // defensive when parsing it. + try { + randomSeed = ((Double) rawLoadBalancingPolicyConfig.get(RANDOM_SEED_KEY)).longValue(); + } catch (RuntimeException e) { + return ConfigOrError.fromError( + Status.UNAVAILABLE.withDescription("unable to parse LB config")); + } } - return ConfigOrError.fromConfig(new ShufflingPickFirstLoadBalancerConfig(randomSeed)); + return ConfigOrError.fromConfig(new ShufflingPickFirstLoadBalancer.Config(randomSeed)); } @Override From 1f4ca10f71af7d7fb2cc679bbd7f4842a4a85dae Mon Sep 17 00:00:00 2001 From: Terry Wilson Date: Mon, 27 Mar 2023 15:22:52 -0700 Subject: [PATCH 3/5] Various review feedback changes --- .../CustomLoadBalanceClient.java | 9 +++----- .../ShufflingPickFirstLoadBalancer.java | 7 +++--- ...hufflingPickFirstLoadBalancerProvider.java | 22 +++++++++++-------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/examples/src/main/java/io/grpc/examples/customloadbalance/CustomLoadBalanceClient.java b/examples/src/main/java/io/grpc/examples/customloadbalance/CustomLoadBalanceClient.java index 6c6481d2795..2cf50a80b0a 100644 --- a/examples/src/main/java/io/grpc/examples/customloadbalance/CustomLoadBalanceClient.java +++ b/examples/src/main/java/io/grpc/examples/customloadbalance/CustomLoadBalanceClient.java @@ -35,16 +35,13 @@ import java.util.logging.Logger; /** - * This client is intended for connecting with the @{link LoadBalanceServer} in the "loadbalance" + * This client is intended for connecting with the {@code LoadBalanceServer} in the "loadbalance" * example. */ public class CustomLoadBalanceClient { private static final Logger logger = Logger.getLogger(CustomLoadBalanceClient.class.getName()); - public static final String exampleScheme = "example"; - public static final String exampleServiceName = "lb.example.grpc.io"; - private final GreeterGrpc.GreeterBlockingStub blockingStub; public CustomLoadBalanceClient(Channel channel) { @@ -71,7 +68,7 @@ public static void main(String[] args) throws Exception { NameResolverRegistry.getDefaultRegistry().register(new ExampleNameResolverProvider()); - String target = String.format("%s:///%s", exampleScheme, exampleServiceName); + String target = "example:///lb.example.grpc.io"; logger.info("Use default first_pick load balance policy"); ManagedChannel channel = Grpc.newChannelBuilder(target, InsecureChannelCredentials.create()) @@ -92,7 +89,7 @@ public static void main(String[] args) throws Exception { // parseLoadBalancingPolicyConfig() gets called. Map serviceConfig = new Gson().fromJson( "{ \"loadBalancingConfig\": " + - " [ { \"example.shuffling_pick_first\": { \"randomSeed\": 123 } } ]" + + " [ { \"grpc.examples.customloadbalance.ShufflingPickFirst\": { \"randomSeed\": 123 } } ]" + "}", Map.class); channel = ManagedChannelBuilder.forTarget(target) diff --git a/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancer.java b/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancer.java index 01e720f0cb6..e76cde3dc36 100644 --- a/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancer.java +++ b/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancer.java @@ -39,14 +39,14 @@ * shuffling of the list of servers so that the first server provided in {@link ResolvedAddresses} * won't necessarily be the server the channel will connect to. */ -public class ShufflingPickFirstLoadBalancer extends LoadBalancer { +class ShufflingPickFirstLoadBalancer extends LoadBalancer { private final Helper helper; private Subchannel subchannel; /** * This class defines the configuration used by this {@link LoadBalancer}. Note that no part of - * the gRPC library is aware of this interface, it will be populated by the + * the gRPC library is aware of this class, it will be populated by the * {@link ShufflingPickFirstLoadBalancerProvider}. */ static class Config { @@ -91,8 +91,7 @@ public void onSubchannelState(ConnectivityStateInfo stateInfo) { }); this.subchannel = subchannel; - helper.updateBalancingState(CONNECTING, - new Picker(PickResult.withSubchannel(this.subchannel))); + helper.updateBalancingState(CONNECTING, new Picker(PickResult.withNoResult())); subchannel.requestConnection(); } else { subchannel.updateAddresses(servers); diff --git a/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancerProvider.java b/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancerProvider.java index a1381186b28..eac1c2c011b 100644 --- a/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancerProvider.java +++ b/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancerProvider.java @@ -30,15 +30,19 @@ public class ShufflingPickFirstLoadBalancerProvider extends LoadBalancerProvider @Override public ConfigOrError parseLoadBalancingPolicyConfig(Map rawLoadBalancingPolicyConfig) { Long randomSeed = null; - if (rawLoadBalancingPolicyConfig.containsKey(RANDOM_SEED_KEY)) { - // The load balancing configuration generally comes from a remote source over the wire, be - // defensive when parsing it. - try { - randomSeed = ((Double) rawLoadBalancingPolicyConfig.get(RANDOM_SEED_KEY)).longValue(); - } catch (RuntimeException e) { - return ConfigOrError.fromError( - Status.UNAVAILABLE.withDescription("unable to parse LB config")); + + // The load balancing configuration generally comes from a remote source over the wire, be + // defensive when parsing it. + try { + if (rawLoadBalancingPolicyConfig.containsKey(RANDOM_SEED_KEY)) { + Object randomSeedObj = rawLoadBalancingPolicyConfig.get("randomSeed"); + if (randomSeedObj instanceof Double) { + randomSeed = ((Double) randomSeedObj).longValue(); + } } + } catch (RuntimeException e) { + return ConfigOrError.fromError( + Status.UNAVAILABLE.withDescription("unable to parse LB config").withCause(e)); } return ConfigOrError.fromConfig(new ShufflingPickFirstLoadBalancer.Config(randomSeed)); } @@ -60,6 +64,6 @@ public int getPriority() { @Override public String getPolicyName() { - return "example.shuffling_pick_first"; + return "grpc.examples.customloadbalance.ShufflingPickFirst"; } } From 315f869df10a5d798237beea70a4b405964da9fd Mon Sep 17 00:00:00 2001 From: Terry Wilson Date: Tue, 28 Mar 2023 09:53:59 -0700 Subject: [PATCH 4/5] More review comment changes --- .../ShufflingPickFirstLoadBalancerProvider.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancerProvider.java b/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancerProvider.java index eac1c2c011b..83486e0cc16 100644 --- a/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancerProvider.java +++ b/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancerProvider.java @@ -34,17 +34,15 @@ public ConfigOrError parseLoadBalancingPolicyConfig(Map rawLoadBalanc // The load balancing configuration generally comes from a remote source over the wire, be // defensive when parsing it. try { - if (rawLoadBalancingPolicyConfig.containsKey(RANDOM_SEED_KEY)) { - Object randomSeedObj = rawLoadBalancingPolicyConfig.get("randomSeed"); - if (randomSeedObj instanceof Double) { - randomSeed = ((Double) randomSeedObj).longValue(); - } + Object randomSeedObj = rawLoadBalancingPolicyConfig.get("randomSeed"); + if (randomSeedObj instanceof Double) { + randomSeed = ((Double) randomSeedObj).longValue(); } + return ConfigOrError.fromConfig(new ShufflingPickFirstLoadBalancer.Config(randomSeed)); } catch (RuntimeException e) { return ConfigOrError.fromError( Status.UNAVAILABLE.withDescription("unable to parse LB config").withCause(e)); } - return ConfigOrError.fromConfig(new ShufflingPickFirstLoadBalancer.Config(randomSeed)); } @Override From 46f899fe9e4e240a897a67469fe179b7637a884b Mon Sep 17 00:00:00 2001 From: Terry Wilson Date: Tue, 28 Mar 2023 11:19:11 -0700 Subject: [PATCH 5/5] Remove orphaned constant --- .../ShufflingPickFirstLoadBalancerProvider.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancerProvider.java b/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancerProvider.java index 83486e0cc16..d72b8ca1c8d 100644 --- a/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancerProvider.java +++ b/examples/src/main/java/io/grpc/examples/customloadbalance/ShufflingPickFirstLoadBalancerProvider.java @@ -25,8 +25,6 @@ public class ShufflingPickFirstLoadBalancerProvider extends LoadBalancerProvider { - private static final String RANDOM_SEED_KEY = "randomSeed"; - @Override public ConfigOrError parseLoadBalancingPolicyConfig(Map rawLoadBalancingPolicyConfig) { Long randomSeed = null;