mirror of https://github.com/grpc/grpc-java.git
Dualstack example (#11451)
This commit is contained in:
parent
40e2b165b7
commit
72a977bf7f
|
|
@ -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
|
||||
```
|
||||
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>
|
||||
|
|
@ -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'
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -45,9 +45,8 @@ public class ExampleNameResolver extends NameResolver {
|
|||
// 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))
|
||||
Arrays.stream(SERVER_PORTS)
|
||||
.mapToObj(port->new InetSocketAddress("localhost",port))
|
||||
.collect(Collectors.toList())
|
||||
)
|
||||
.build();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,8 +55,7 @@ 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))
|
||||
channel = ManagedChannelBuilder.forTarget(channelTarget)
|
||||
.defaultLoadBalancingPolicy("round_robin")
|
||||
.usePlaintext()
|
||||
.build();
|
||||
|
|
|
|||
Loading…
Reference in New Issue