Dualstack example (#11451)

This commit is contained in:
Larry Safran 2024-08-08 15:59:57 -07:00 committed by GitHub
parent 40e2b165b7
commit 72a977bf7f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 694 additions and 31 deletions

View File

@ -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
```

View File

@ -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)
}
}
}

View File

@ -0,0 +1,122 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>io.grpc</groupId>
<artifactId>example-dualstack</artifactId>
<packaging>jar</packaging>
<!-- Feel free to delete the comment at the end of these lines. It is just
for safely updating the version in our release process. -->
<version>1.67.0-SNAPSHOT</version><!-- CURRENT_GRPC_VERSION -->
<name>example-dualstack</name>
<url>https://github.com/grpc/grpc-java</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<grpc.version>1.67.0-SNAPSHOT</grpc.version><!-- CURRENT_GRPC_VERSION -->
<protoc.version>3.25.3</protoc.version>
<!-- required for jdk9 -->
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-bom</artifactId>
<version>${grpc.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-services</artifactId>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>annotations-api</artifactId>
<version>6.0.53</version>
<scope>provided</scope> <!-- not needed at runtime -->
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty-shaded</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-testing</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.7.1</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.4.1</version>
<executions>
<execution>
<id>enforce</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireUpperBoundDeps/>
</rules>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -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'

View File

@ -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());
}
}

View File

@ -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<Server> 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<HelloReply> 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();
}
}
}

View File

@ -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<String, List<List<SocketAddress>>> addrStore =
ImmutableMap.<String, List<List<SocketAddress>>>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<SocketAddress> 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<List<SocketAddress>> addresses = addrStore.get(uri.getPath().substring(1));
try {
List<EquivalentAddressGroup> eagList = new ArrayList<>();
for (List<SocketAddress> 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));
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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.<String,List<InetSocketAddress>>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();
}

View File

@ -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<Server> 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<HelloReply> 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();
}

View File

@ -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++) {