diff --git a/examples/example-dualstack/README.md b/examples/example-dualstack/README.md
new file mode 100644
index 0000000000..6c191661d1
--- /dev/null
+++ b/examples/example-dualstack/README.md
@@ -0,0 +1,54 @@
+# gRPC Dualstack Example
+
+The dualstack example uses a custom name resolver that provides both IPv4 and IPv6 localhost
+endpoints for each of 3 server instances. The client will first use the default name resolver and
+load balancers which will only connect tot he first server. It will then use the
+custom name resolver with round robin to connect to each of the servers in turn. The 3 instances
+of the server will bind respectively to: both IPv4 and IPv6, IPv4 only, and IPv6 only.
+
+The example requires grpc-java to already be built. You are strongly encouraged
+to check out a git release tag, since there will already be a build of grpc
+available. Otherwise, you must follow [COMPILING](../../COMPILING.md).
+
+### Build the example
+
+To build the dualstack example server and client. From the
+ `grpc-java/examples/example-dualstack` directory run:
+
+```bash
+$ ../gradlew installDist
+```
+
+This creates the scripts
+`build/install/example-dualstack/bin/dual-stack-server`
+ and `build/install/example-dualstack/bin/dual-stack-client`.
+
+To run the dualstack example, run the server with:
+
+```bash
+$ ./build/install/example-dualstack/bin/dual-stack-server
+```
+
+And in a different terminal window run the client.
+
+```bash
+$ ./build/install/example-dualstack/bin/dual-stack-client
+```
+
+### Maven
+
+If you prefer to use Maven:
+
+Run in the example-debug directory:
+
+```bash
+$ mvn verify
+$ # Run the server in one terminal
+$ mvn exec:java -Dexec.mainClass=io.grpc.examples.dualstack.DualStackServer
+```
+
+```bash
+$ # In another terminal run the client
+$ mvn exec:java -Dexec.mainClass=io.grpc.examples.dualstack.DualStackClient
+```
+
diff --git a/examples/example-dualstack/build.gradle b/examples/example-dualstack/build.gradle
new file mode 100644
index 0000000000..32b35af8a8
--- /dev/null
+++ b/examples/example-dualstack/build.gradle
@@ -0,0 +1,79 @@
+plugins {
+ id 'application' // Provide convenience executables for trying out the examples.
+ id 'java'
+
+ id "com.google.protobuf" version "0.9.4"
+
+ // Generate IntelliJ IDEA's .idea & .iml project files
+ id 'idea'
+}
+
+repositories {
+ maven { // The google mirror is less flaky than mavenCentral()
+ url "https://maven-central.storage-download.googleapis.com/maven2/" }
+ mavenCentral()
+ mavenLocal()
+}
+
+java {
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
+}
+
+// IMPORTANT: You probably want the non-SNAPSHOT version of gRPC. Make sure you
+// are looking at a tagged version of the example and not "master"!
+
+// Feel free to delete the comment at the next line. It is just for safely
+// updating the version in our release process.
+def grpcVersion = '1.67.0-SNAPSHOT' // CURRENT_GRPC_VERSION
+def protobufVersion = '3.25.3'
+
+dependencies {
+ implementation "io.grpc:grpc-protobuf:${grpcVersion}"
+ implementation "io.grpc:grpc-netty:${grpcVersion}"
+ implementation "io.grpc:grpc-stub:${grpcVersion}"
+ implementation "io.grpc:grpc-services:${grpcVersion}"
+ compileOnly "org.apache.tomcat:annotations-api:6.0.53"
+}
+
+protobuf {
+ protoc {
+ artifact = "com.google.protobuf:protoc:${protobufVersion}"
+ }
+ plugins {
+ grpc {
+ artifact = "io.grpc:protoc-gen-grpc-java:${grpcVersion}"
+ }
+ }
+ generateProtoTasks {
+ all()*.plugins {
+ grpc {}
+ }
+ }
+}
+
+startScripts.enabled = false
+
+task DualStackClient(type: CreateStartScripts) {
+ mainClass = 'io.grpc.examples.dualstack.DualStackClient'
+ applicationName = 'dual-stack-client'
+ outputDir = new File(project.buildDir, 'tmp/scripts/' + name)
+ classpath = startScripts.classpath
+}
+
+task DualStackServer(type: CreateStartScripts) {
+ mainClass = 'io.grpc.examples.dualstack.DualStackServer'
+ applicationName = 'dual-stack-server'
+ outputDir = new File(project.buildDir, 'tmp/scripts/' + name)
+ classpath = startScripts.classpath
+}
+
+application {
+ applicationDistribution.into('bin') {
+ from(DualStackClient)
+ from(DualStackServer)
+ filePermissions {
+ unix(0755)
+ }
+ }
+}
diff --git a/examples/example-dualstack/pom.xml b/examples/example-dualstack/pom.xml
new file mode 100644
index 0000000000..710b48ee61
--- /dev/null
+++ b/examples/example-dualstack/pom.xml
@@ -0,0 +1,122 @@
+
+ 4.0.0
+ io.grpc
+ example-dualstack
+ jar
+
+ 1.67.0-SNAPSHOT
+ example-dualstack
+ https://github.com/grpc/grpc-java
+
+
+ UTF-8
+ 1.67.0-SNAPSHOT
+ 3.25.3
+
+ 1.8
+ 1.8
+
+
+
+
+
+ io.grpc
+ grpc-bom
+ ${grpc.version}
+ pom
+ import
+
+
+
+
+
+
+ io.grpc
+ grpc-services
+
+
+ io.grpc
+ grpc-protobuf
+
+
+ io.grpc
+ grpc-stub
+
+
+ io.grpc
+ grpc-netty
+
+
+ org.apache.tomcat
+ annotations-api
+ 6.0.53
+ provided
+
+
+ io.grpc
+ grpc-netty-shaded
+ runtime
+
+
+ junit
+ junit
+ 4.13.2
+ test
+
+
+ io.grpc
+ grpc-testing
+ test
+
+
+
+
+
+
+ kr.motd.maven
+ os-maven-plugin
+ 1.7.1
+
+
+
+
+ org.xolstice.maven.plugins
+ protobuf-maven-plugin
+ 0.6.1
+
+ com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}
+ grpc-java
+ io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}
+
+
+
+
+ compile
+ compile-custom
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+ 1.4.1
+
+
+ enforce
+
+ enforce
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/example-dualstack/settings.gradle b/examples/example-dualstack/settings.gradle
new file mode 100644
index 0000000000..0aae8f7304
--- /dev/null
+++ b/examples/example-dualstack/settings.gradle
@@ -0,0 +1,10 @@
+pluginManagement {
+ repositories {
+ maven { // The google mirror is less flaky than mavenCentral()
+ url "https://maven-central.storage-download.googleapis.com/maven2/"
+ }
+ gradlePluginPortal()
+ }
+}
+
+rootProject.name = 'example-dualstack'
diff --git a/examples/example-dualstack/src/main/java/io/grpc/examples/dualstack/DualStackClient.java b/examples/example-dualstack/src/main/java/io/grpc/examples/dualstack/DualStackClient.java
new file mode 100644
index 0000000000..b9993a524d
--- /dev/null
+++ b/examples/example-dualstack/src/main/java/io/grpc/examples/dualstack/DualStackClient.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2024 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.dualstack;
+
+import io.grpc.Channel;
+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 java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A client that requests greetings from the {@link DualStackServer}.
+ * First it sends 5 requests using the default nameresolver and load balancer.
+ * Then it sends 10 requests using the example nameresolver and round robin load balancer. These
+ * requests are evenly distributed among the 3 servers rather than favoring the server listening
+ * on both addresses because the ExampleDualStackNameResolver groups the 3 servers as 3 endpoints
+ * each with 2 addresses.
+ */
+public class DualStackClient {
+ public static final String channelTarget = "example:///lb.example.grpc.io";
+ private static final Logger logger = Logger.getLogger(DualStackClient.class.getName());
+ private final GreeterGrpc.GreeterBlockingStub blockingStub;
+
+ public DualStackClient(Channel channel) {
+ blockingStub = GreeterGrpc.newBlockingStub(channel);
+ }
+
+ public static void main(String[] args) throws Exception {
+ NameResolverRegistry.getDefaultRegistry()
+ .register(new ExampleDualStackNameResolverProvider());
+
+ logger.info("\n **** Use default DNS resolver ****");
+ ManagedChannel channel = ManagedChannelBuilder.forTarget("localhost:50051")
+ .usePlaintext()
+ .build();
+ try {
+ DualStackClient client = new DualStackClient(channel);
+ for (int i = 0; i < 5; i++) {
+ client.greet("request:" + i);
+ }
+ } finally {
+ channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
+ }
+
+ logger.info("\n **** Change to use example name resolver ****");
+ /*
+ Dial to "example:///resolver.example.grpc.io", use {@link ExampleNameResolver} to create connection
+ "resolver.example.grpc.io" is converted to {@link java.net.URI.path}
+ */
+ channel = ManagedChannelBuilder.forTarget(channelTarget)
+ .defaultLoadBalancingPolicy("round_robin")
+ .usePlaintext()
+ .build();
+ try {
+ DualStackClient client = new DualStackClient(channel);
+ for (int i = 0; i < 10; i++) {
+ client.greet("request:" + i);
+ }
+ } finally {
+ channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
+ }
+ }
+
+ 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());
+ }
+}
diff --git a/examples/example-dualstack/src/main/java/io/grpc/examples/dualstack/DualStackServer.java b/examples/example-dualstack/src/main/java/io/grpc/examples/dualstack/DualStackServer.java
new file mode 100644
index 0000000000..43b21e963f
--- /dev/null
+++ b/examples/example-dualstack/src/main/java/io/grpc/examples/dualstack/DualStackServer.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2024 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.dualstack;
+
+import io.grpc.Server;
+import io.grpc.ServerBuilder;
+import io.grpc.examples.helloworld.GreeterGrpc;
+import io.grpc.examples.helloworld.HelloReply;
+import io.grpc.examples.helloworld.HelloRequest;
+import io.grpc.netty.NettyServerBuilder;
+import io.grpc.stub.StreamObserver;
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Logger;
+
+/**
+ * Starts 3 different greeter services each on its own port, but all for localhost.
+ * The first service listens on both IPv4 and IPv6,
+ * the second on just IPv4, and the third on just IPv6.
+ */
+public class DualStackServer {
+ private static final Logger logger = Logger.getLogger(DualStackServer.class.getName());
+ private List servers;
+
+ public static void main(String[] args) throws IOException, InterruptedException {
+ final DualStackServer server = new DualStackServer();
+ server.start();
+ server.blockUntilShutdown();
+ }
+
+ private void start() throws IOException {
+ InetSocketAddress inetSocketAddress;
+
+ servers = new ArrayList<>();
+ int[] serverPorts = ExampleDualStackNameResolver.SERVER_PORTS;
+ for (int i = 0; i < serverPorts.length; i++ ) {
+ String addressType;
+ int port = serverPorts[i];
+ ServerBuilder> serverBuilder;
+ switch (i) {
+ case 0:
+ serverBuilder = ServerBuilder.forPort(port); // bind to both IPv4 and IPv6
+ addressType = "both IPv4 and IPv6";
+ break;
+ case 1:
+ // bind to IPv4 only
+ inetSocketAddress = new InetSocketAddress("127.0.0.1", port);
+ serverBuilder = NettyServerBuilder.forAddress(inetSocketAddress);
+ addressType = "IPv4 only";
+ break;
+ case 2:
+ // bind to IPv6 only
+ inetSocketAddress = new InetSocketAddress("::1", port);
+ serverBuilder = NettyServerBuilder.forAddress(inetSocketAddress);
+ addressType = "IPv6 only";
+ break;
+ default:
+ throw new IllegalStateException("Unexpected value: " + i);
+ }
+
+ servers.add(serverBuilder
+ .addService(new GreeterImpl(port, addressType))
+ .build()
+ .start());
+ logger.info("Server started, listening on " + port);
+ }
+ Runtime.getRuntime().addShutdownHook(new Thread(() -> {
+ System.err.println("*** shutting down gRPC server since JVM is shutting down");
+ try {
+ DualStackServer.this.stop();
+ } catch (InterruptedException e) {
+ e.printStackTrace(System.err);
+ }
+ System.err.println("*** server shut down");
+ }));
+ }
+
+ private void stop() throws InterruptedException {
+ for (Server server : servers) {
+ server.shutdown().awaitTermination(30, TimeUnit.SECONDS);
+ }
+ }
+
+ private void blockUntilShutdown() throws InterruptedException {
+ for (Server server : servers) {
+ server.awaitTermination();
+ }
+ }
+
+ static class GreeterImpl extends GreeterGrpc.GreeterImplBase {
+
+ int port;
+ String addressType;
+
+ public GreeterImpl(int port, String addressType) {
+ this.port = port;
+ this.addressType = addressType;
+ }
+
+ @Override
+ public void sayHello(HelloRequest req, StreamObserver responseObserver) {
+ String msg = String.format("Hello %s from server<%d> type: %s",
+ req.getName(), this.port, addressType);
+ HelloReply reply = HelloReply.newBuilder().setMessage(msg).build();
+ responseObserver.onNext(reply);
+ responseObserver.onCompleted();
+ }
+ }
+}
diff --git a/examples/example-dualstack/src/main/java/io/grpc/examples/dualstack/ExampleDualStackNameResolver.java b/examples/example-dualstack/src/main/java/io/grpc/examples/dualstack/ExampleDualStackNameResolver.java
new file mode 100644
index 0000000000..70675b3de3
--- /dev/null
+++ b/examples/example-dualstack/src/main/java/io/grpc/examples/dualstack/ExampleDualStackNameResolver.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2024 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.dualstack;
+
+import com.google.common.collect.ImmutableMap;
+import io.grpc.EquivalentAddressGroup;
+import io.grpc.NameResolver;
+import io.grpc.Status;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * A fake name resolver that resolves to a hard-coded list of 3 endpoints (EquivalentAddressGropu)
+ * each with 2 addresses (one IPv4 and one IPv6).
+ */
+public class ExampleDualStackNameResolver extends NameResolver {
+ static public final int[] SERVER_PORTS = {50051, 50052, 50053};
+
+ // This is a fake name resolver, so we just hard code the address here.
+ private static final ImmutableMap>> addrStore =
+ ImmutableMap.>>builder()
+ .put("lb.example.grpc.io",
+ Arrays.stream(SERVER_PORTS)
+ .mapToObj(port -> getLocalAddrs(port))
+ .collect(Collectors.toList())
+ )
+ .build();
+
+ private Listener2 listener;
+
+ private final URI uri;
+
+ public ExampleDualStackNameResolver(URI targetUri) {
+ this.uri = targetUri;
+ }
+
+ private static List getLocalAddrs(int port) {
+ return Arrays.asList(
+ new InetSocketAddress("127.0.0.1", port),
+ new InetSocketAddress("::1", port));
+ }
+
+ @Override
+ public String getServiceAuthority() {
+ return uri.getPath().substring(1);
+ }
+
+ @Override
+ public void shutdown() {
+ }
+
+ @Override
+ public void start(Listener2 listener) {
+ this.listener = listener;
+ this.resolve();
+ }
+
+ @Override
+ public void refresh() {
+ this.resolve();
+ }
+
+ private void resolve() {
+ List> addresses = addrStore.get(uri.getPath().substring(1));
+ try {
+ List eagList = new ArrayList<>();
+ for (List endpoint : addresses) {
+ // every server is an EquivalentAddressGroup, so they can be accessed randomly
+ eagList.add(new EquivalentAddressGroup(endpoint));
+ }
+
+ this.listener.onResult(ResolutionResult.newBuilder().setAddresses(eagList).build());
+ } catch (Exception e){
+ // when error occurs, notify listener
+ this.listener.onError(Status.UNAVAILABLE.withDescription("Unable to resolve host ").withCause(e));
+ }
+ }
+
+}
diff --git a/examples/example-dualstack/src/main/java/io/grpc/examples/dualstack/ExampleDualStackNameResolverProvider.java b/examples/example-dualstack/src/main/java/io/grpc/examples/dualstack/ExampleDualStackNameResolverProvider.java
new file mode 100644
index 0000000000..a01d68aca3
--- /dev/null
+++ b/examples/example-dualstack/src/main/java/io/grpc/examples/dualstack/ExampleDualStackNameResolverProvider.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2024 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.dualstack;
+
+import io.grpc.NameResolver;
+import io.grpc.NameResolverProvider;
+
+import java.net.URI;
+
+public class ExampleDualStackNameResolverProvider extends NameResolverProvider {
+ public static final String exampleScheme = "example";
+
+ @Override
+ public NameResolver newNameResolver(URI targetUri, NameResolver.Args args) {
+ return new ExampleDualStackNameResolver(targetUri);
+ }
+
+ @Override
+ protected boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ protected int priority() {
+ return 5;
+ }
+
+ @Override
+ // gRPC choose the first NameResolverProvider that supports the target URI scheme.
+ public String getDefaultScheme() {
+ return exampleScheme;
+ }
+}
diff --git a/examples/example-dualstack/src/main/proto/helloworld/helloworld.proto b/examples/example-dualstack/src/main/proto/helloworld/helloworld.proto
new file mode 100644
index 0000000000..c60d9416f1
--- /dev/null
+++ b/examples/example-dualstack/src/main/proto/helloworld/helloworld.proto
@@ -0,0 +1,37 @@
+// Copyright 2015 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.
+syntax = "proto3";
+
+option java_multiple_files = true;
+option java_package = "io.grpc.examples.helloworld";
+option java_outer_classname = "HelloWorldProto";
+option objc_class_prefix = "HLW";
+
+package helloworld;
+
+// The greeting service definition.
+service Greeter {
+ // Sends a greeting
+ rpc SayHello (HelloRequest) returns (HelloReply) {}
+}
+
+// The request message containing the user's name.
+message HelloRequest {
+ string name = 1;
+}
+
+// The response message containing the greetings
+message HelloReply {
+ string message = 1;
+}
diff --git a/examples/src/main/java/io/grpc/examples/loadbalance/ExampleNameResolver.java b/examples/src/main/java/io/grpc/examples/loadbalance/ExampleNameResolver.java
index f562f0ac10..6ef327ade8 100644
--- a/examples/src/main/java/io/grpc/examples/loadbalance/ExampleNameResolver.java
+++ b/examples/src/main/java/io/grpc/examples/loadbalance/ExampleNameResolver.java
@@ -28,12 +28,12 @@ import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
-import java.util.stream.Stream;
import static io.grpc.examples.loadbalance.LoadBalanceClient.exampleServiceName;
public class ExampleNameResolver extends NameResolver {
+ static private final int[] SERVER_PORTS = {50051, 50052, 50053};
private Listener2 listener;
private final URI uri;
@@ -44,12 +44,11 @@ public class ExampleNameResolver extends NameResolver {
this.uri = targetUri;
// This is a fake name resolver, so we just hard code the address here.
addrStore = ImmutableMap.>builder()
- .put(exampleServiceName,
- Stream.iterate(LoadBalanceServer.startPort,p->p+1)
- .limit(LoadBalanceServer.serverCount)
- .map(port->new InetSocketAddress("localhost",port))
- .collect(Collectors.toList())
- )
+ .put(exampleServiceName,
+ Arrays.stream(SERVER_PORTS)
+ .mapToObj(port->new InetSocketAddress("localhost",port))
+ .collect(Collectors.toList())
+ )
.build();
}
diff --git a/examples/src/main/java/io/grpc/examples/loadbalance/LoadBalanceServer.java b/examples/src/main/java/io/grpc/examples/loadbalance/LoadBalanceServer.java
index c97d209497..85ae92a537 100644
--- a/examples/src/main/java/io/grpc/examples/loadbalance/LoadBalanceServer.java
+++ b/examples/src/main/java/io/grpc/examples/loadbalance/LoadBalanceServer.java
@@ -24,23 +24,24 @@ import io.grpc.examples.helloworld.HelloRequest;
import io.grpc.stub.StreamObserver;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
public class LoadBalanceServer {
private static final Logger logger = Logger.getLogger(LoadBalanceServer.class.getName());
- static public final int serverCount = 3;
- static public final int startPort = 50051;
- private Server[] servers;
+ static final int[] SERVER_PORTS = {50051, 50052, 50053};
+ private List servers;
private void start() throws IOException {
- servers = new Server[serverCount];
- for (int i = 0; i < serverCount; i++) {
- int port = startPort + i;
- servers[i] = ServerBuilder.forPort(port)
+ servers = new ArrayList<>();
+ for (int port : SERVER_PORTS) {
+ servers.add(
+ ServerBuilder.forPort(port)
.addService(new GreeterImpl(port))
.build()
- .start();
+ .start());
logger.info("Server started, listening on " + port);
}
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
@@ -55,18 +56,14 @@ public class LoadBalanceServer {
}
private void stop() throws InterruptedException {
- for (int i = 0; i < serverCount; i++) {
- if (servers[i] != null) {
- servers[i].shutdown().awaitTermination(30, TimeUnit.SECONDS);
- }
+ for (Server server : servers) {
+ server.shutdown().awaitTermination(30, TimeUnit.SECONDS);
}
}
private void blockUntilShutdown() throws InterruptedException {
- for (int i = 0; i < serverCount; i++) {
- if (servers[i] != null) {
- servers[i].awaitTermination();
- }
+ for (Server server : servers) {
+ server.awaitTermination();
}
}
@@ -86,7 +83,8 @@ public class LoadBalanceServer {
@Override
public void sayHello(HelloRequest req, StreamObserver responseObserver) {
- HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName() + " from server<" + this.port + ">").build();
+ HelloReply reply = HelloReply.newBuilder()
+ .setMessage("Hello " + req.getName() + " from server<" + this.port + ">").build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
}
diff --git a/examples/src/main/java/io/grpc/examples/nameresolve/NameResolveClient.java b/examples/src/main/java/io/grpc/examples/nameresolve/NameResolveClient.java
index ac6fdd3254..9aaccbe109 100644
--- a/examples/src/main/java/io/grpc/examples/nameresolve/NameResolveClient.java
+++ b/examples/src/main/java/io/grpc/examples/nameresolve/NameResolveClient.java
@@ -26,8 +26,7 @@ import java.util.logging.Level;
import java.util.logging.Logger;
public class NameResolveClient {
- public static final String exampleScheme = "example";
- public static final String exampleServiceName = "lb.example.grpc.io";
+ public static final String channelTarget = "example:///lb.example.grpc.io";
private static final Logger logger = Logger.getLogger(NameResolveClient.class.getName());
private final GreeterGrpc.GreeterBlockingStub blockingStub;
@@ -56,11 +55,10 @@ public class NameResolveClient {
Dial to "example:///resolver.example.grpc.io", use {@link ExampleNameResolver} to create connection
"resolver.example.grpc.io" is converted to {@link java.net.URI.path}
*/
- channel = ManagedChannelBuilder.forTarget(
- String.format("%s:///%s", exampleScheme, exampleServiceName))
- .defaultLoadBalancingPolicy("round_robin")
- .usePlaintext()
- .build();
+ channel = ManagedChannelBuilder.forTarget(channelTarget)
+ .defaultLoadBalancingPolicy("round_robin")
+ .usePlaintext()
+ .build();
try {
NameResolveClient client = new NameResolveClient(channel);
for (int i = 0; i < 5; i++) {