From d2f52fe71d250e00456ea2ccf1529ca902dd3717 Mon Sep 17 00:00:00 2001 From: Eric Anderson Date: Wed, 22 Jan 2020 13:28:43 -0800 Subject: [PATCH] examples: Allow passing target and simplify lifecycle The target can be provided on the command line to avoid needing to recompile the example just to change where the server is located. We use a target instead of addresses as that is the approach we have wanted to encourage for a while since it allows choosing alternative name resolvers. We typically encourage injecting Channels, not ManagedChannels, which has the added benefit of simplifying the example. Less indirection makes for a better example. Swapping to target string could be done to examples-tls and examples-gauth as well, but it would be much more invasive to the tls example and the gauth example would need proper testing after the change. --- .../examples/helloworld/HelloWorldClient.java | 61 +++++++++++-------- .../examples/routeguide/RouteGuideClient.java | 30 ++++----- .../routeguide/RouteGuideClientTest.java | 10 +-- 3 files changed, 55 insertions(+), 46 deletions(-) diff --git a/examples/src/main/java/io/grpc/examples/helloworld/HelloWorldClient.java b/examples/src/main/java/io/grpc/examples/helloworld/HelloWorldClient.java index 322c0f25f72..d00bca1e216 100644 --- a/examples/src/main/java/io/grpc/examples/helloworld/HelloWorldClient.java +++ b/examples/src/main/java/io/grpc/examples/helloworld/HelloWorldClient.java @@ -16,6 +16,7 @@ package io.grpc.examples.helloworld; +import io.grpc.Channel; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import io.grpc.StatusRuntimeException; @@ -29,26 +30,15 @@ public class HelloWorldClient { private static final Logger logger = Logger.getLogger(HelloWorldClient.class.getName()); - private final ManagedChannel channel; private final GreeterGrpc.GreeterBlockingStub blockingStub; - /** Construct client connecting to HelloWorld server at {@code host:port}. */ - public HelloWorldClient(String host, int port) { - this(ManagedChannelBuilder.forAddress(host, port) - // Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid - // needing certificates. - .usePlaintext() - .build()); - } - /** Construct client for accessing HelloWorld server using the existing channel. */ - HelloWorldClient(ManagedChannel channel) { - this.channel = channel; - blockingStub = GreeterGrpc.newBlockingStub(channel); - } + public HelloWorldClient(Channel channel) { + // 'channel' here is a Channel, not a ManagedChannel, so it is not this code's responsibility to + // shut it down. - public void shutdown() throws InterruptedException { - channel.shutdown().awaitTermination(5, TimeUnit.SECONDS); + // Passing Channels to code makes code easier to test and makes it easier to reuse Channels. + blockingStub = GreeterGrpc.newBlockingStub(channel); } /** Say hello to server. */ @@ -67,20 +57,43 @@ public void greet(String name) { /** * Greet server. If provided, the first element of {@code args} is the name to use in the - * greeting. + * greeting. The second argument is the target server. */ public static void main(String[] args) throws Exception { + String user = "world"; // Access a service running on the local machine on port 50051 - HelloWorldClient client = new HelloWorldClient("localhost", 50051); - try { - String user = "world"; - // Use the arg as the name to greet if provided - if (args.length > 0) { - user = args[0]; + String target = "localhost:50051"; + // Allow passing in the user and target strings as command line arguments + if (args.length > 0) { + if ("--help".equals(args[0])) { + System.err.println("Usage: [name [target]]"); + System.err.println(""); + System.err.println(" name The name you wish to be greeted by. Defaults to " + user); + System.err.println(" target The server to connect to. Defaults to " + target); + System.exit(1); } + user = args[0]; + } + if (args.length > 1) { + target = args[1]; + } + + // Create a communication channel to the server, known as a Channel. Channels are thread-safe + // and reusable. It is common to create channels at the beginning of your application and reuse + // them until the application shuts down. + ManagedChannel channel = ManagedChannelBuilder.forTarget(target) + // Channels are secure by default (via SSL/TLS). For the example we disable TLS to avoid + // needing certificates. + .usePlaintext() + .build(); + try { + HelloWorldClient client = new HelloWorldClient(channel); client.greet(user); } finally { - client.shutdown(); + // ManagedChannels use resources like threads and TCP connections. To prevent leaking these + // resources the channel should be shut down when it will no longer be used. If it may be used + // again leave it running. + channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS); } } } diff --git a/examples/src/main/java/io/grpc/examples/routeguide/RouteGuideClient.java b/examples/src/main/java/io/grpc/examples/routeguide/RouteGuideClient.java index 6e22ba52861..b3f46b48bf9 100644 --- a/examples/src/main/java/io/grpc/examples/routeguide/RouteGuideClient.java +++ b/examples/src/main/java/io/grpc/examples/routeguide/RouteGuideClient.java @@ -18,6 +18,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.protobuf.Message; +import io.grpc.Channel; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import io.grpc.Status; @@ -40,29 +41,18 @@ public class RouteGuideClient { private static final Logger logger = Logger.getLogger(RouteGuideClient.class.getName()); - private final ManagedChannel channel; private final RouteGuideBlockingStub blockingStub; private final RouteGuideStub asyncStub; private Random random = new Random(); private TestHelper testHelper; - /** Construct client for accessing RouteGuide server at {@code host:port}. */ - public RouteGuideClient(String host, int port) { - this(ManagedChannelBuilder.forAddress(host, port).usePlaintext()); - } - /** Construct client for accessing RouteGuide server using the existing channel. */ - public RouteGuideClient(ManagedChannelBuilder channelBuilder) { - channel = channelBuilder.build(); + public RouteGuideClient(Channel channel) { blockingStub = RouteGuideGrpc.newBlockingStub(channel); asyncStub = RouteGuideGrpc.newStub(channel); } - public void shutdown() throws InterruptedException { - channel.shutdown().awaitTermination(5, TimeUnit.SECONDS); - } - /** * Blocking unary call example. Calls getFeature and prints the response. */ @@ -250,6 +240,17 @@ public void onCompleted() { /** Issues several different requests and then exits. */ public static void main(String[] args) throws InterruptedException { + String target = "localhost:8980"; + if (args.length > 0) { + if ("--help".equals(args[0])) { + System.err.println("Usage: [target]"); + System.err.println(""); + System.err.println(" target The server to connect to. Defaults to " + target); + System.exit(1); + } + target = args[0]; + } + List features; try { features = RouteGuideUtil.parseFeatures(RouteGuideUtil.getDefaultFeaturesFile()); @@ -258,8 +259,9 @@ public static void main(String[] args) throws InterruptedException { return; } - RouteGuideClient client = new RouteGuideClient("localhost", 8980); + ManagedChannel channel = ManagedChannelBuilder.forTarget(target).usePlaintext().build(); try { + RouteGuideClient client = new RouteGuideClient(channel); // Looking for a valid feature client.getFeature(409146138, -746188906); @@ -279,7 +281,7 @@ public static void main(String[] args) throws InterruptedException { client.warning("routeChat can not finish within 1 minutes"); } } finally { - client.shutdown(); + channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS); } } diff --git a/examples/src/test/java/io/grpc/examples/routeguide/RouteGuideClientTest.java b/examples/src/test/java/io/grpc/examples/routeguide/RouteGuideClientTest.java index fbe1ae20b1d..27fe5b70c9b 100644 --- a/examples/src/test/java/io/grpc/examples/routeguide/RouteGuideClientTest.java +++ b/examples/src/test/java/io/grpc/examples/routeguide/RouteGuideClientTest.java @@ -40,7 +40,6 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; -import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -96,16 +95,11 @@ public void setUp() throws Exception { // Use a mutable service registry for later registering the service impl for each test case. grpcCleanup.register(InProcessServerBuilder.forName(serverName) .fallbackHandlerRegistry(serviceRegistry).directExecutor().build().start()); - client = - new RouteGuideClient(InProcessChannelBuilder.forName(serverName).directExecutor()); + client = new RouteGuideClient(grpcCleanup.register( + InProcessChannelBuilder.forName(serverName).directExecutor().build())); client.setTestHelper(testHelper); } - @After - public void tearDown() throws Exception { - client.shutdown(); - } - /** * Example for testing blocking unary call. */