aboutsummaryrefslogtreecommitdiff
path: root/documentation
diff options
context:
space:
mode:
authorEric Gribkoff <ericgribkoff@google.com>2017-03-13 13:07:38 -0700
committerGitHub <noreply@github.com>2017-03-13 13:07:38 -0700
commit884b65a891976168211d86ec7d7844a82b9c1ee9 (patch)
tree5412af53a1afd9dd3801f1ddd3850fa8effc4b5b /documentation
parent7c3f664e9b5148a615128541a344133420d6147d (diff)
downloadgrpc-grpc-java-884b65a891976168211d86ec7d7844a82b9c1ee9.tar.gz
documentation: monitoring service tutorial
Diffstat (limited to 'documentation')
-rw-r--r--documentation/monitoring-service-tutorial.md234
1 files changed, 234 insertions, 0 deletions
diff --git a/documentation/monitoring-service-tutorial.md b/documentation/monitoring-service-tutorial.md
new file mode 100644
index 000000000..61256ba6d
--- /dev/null
+++ b/documentation/monitoring-service-tutorial.md
@@ -0,0 +1,234 @@
+# gRPC Monitoring Service Tutorial
+
+***Note:*** *The monitoring service requires the `instrumentation-java` library
+implementations, which are still being developed. The steps in this tutorial
+will not work until the `instrumentation-java` implementation is released.*
+
+The gRPC monitoring service provides access to metrics such as RPC latencies,
+bytes sent and received, and error counts. By default, the monitoring service
+will expose metrics recorded by all gRPC clients and servers in the same
+application.
+
+# Requirement: instrumentation-java
+
+For the monitoring service to start, gRPC must have access to the
+`instrumentation-java` library. By default, gRPC depends only on the
+`instrumentation` APIs. The actual implementations have not yet been released.
+
+**TODO(ericgribkoff):** Add instructions for including instrumentation in
+`build.gradle` once `instrumentation-java` is released.
+
+# Adding the service to a server
+
+The gRPC monitoring service is implemented by
+`io.grpc.services.MonitoringService` in the `grpc-services` package. The service
+itself is defined in
+[`grpc/instrumentation/v1alpha/monitoring.proto`](https://github.com/grpc/grpc-proto/blob/master/grpc/instrumentation/v1alpha/monitoring.proto).
+Currently only the `GetCanonicalRpcStats` method is supported.
+
+To run the monitoring service, it must be added to a server. While the
+monitoring service can be added to any existing server, keep in mind that the
+service will be exposing potentially sensitive information about your
+applications. It is up to you to make sure that the server hosting the
+monitoring service is secure. Typically, this will mean running a separate gRPC
+server to host the monitoring service.
+
+To start the `HelloWorldServer` with a second server on port `50052` for the
+monitoring service, we need to make the following changes:
+
+```diff
+--- a/examples/build.gradle
++++ b/examples/build.gradle
+@@ -27,6 +27,7 @@
+ dependencies {
+ compile "io.grpc:grpc-netty:${grpcVersion}"
+ compile "io.grpc:grpc-protobuf:${grpcVersion}"
++ compile "io.grpc:grpc-services:${grpcVersion}"
+ compile "io.grpc:grpc-stub:${grpcVersion}"
+
+ testCompile "junit:junit:4.11"
+--- a/examples/src/main/java/io/grpc/examples/helloworld/HelloWorldServer.java
++++ b/examples/src/main/java/io/grpc/examples/helloworld/HelloWorldServer.java
+@@ -33,6 +33,8 @@ package io.grpc.examples.helloworld;
+
+ import io.grpc.Server;
+ import io.grpc.ServerBuilder;
++import io.grpc.protobuf.services.ProtoReflectionService;
++import io.grpc.services.MonitoringService;
+ import io.grpc.stub.StreamObserver;
+ import java.io.IOException;
+ import java.util.logging.Logger;
+@@ -44,6 +46,7 @@ public class HelloWorldServer {
+ private static final Logger logger = Logger.getLogger(HelloWorldServer.class.getName());
+
+ private Server server;
++ private Server monitoringServer;
+
+ private void start() throws IOException {
+ /* The port on which the server should run */
+@@ -53,6 +56,14 @@ public class HelloWorldServer {
+ .build()
+ .start();
+ logger.info("Server started, listening on " + port);
++ int monitoringPort = 50052;
++ /* This will throw an exception if StatsManager from instrumentation-java is unavailable */
++ monitoringServer = ServerBuilder.forPort(monitoringPort)
++ .addService(MonitoringService.getInstance())
++ .addService(ProtoReflectionService.newInstance())
++ .build()
++ .start();
++ logger.info("Monitoring server started, listening on " + monitoringPort);
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ @Override
+ public void run() {
+@@ -68,6 +79,9 @@ public class HelloWorldServer {
+ if (server != null) {
+ server.shutdown();
+ }
++ if (monitoringServer != null) {
++ monitoringServer.shutdown();
++ }
+ }
+
+ /**
+```
+
+Note that the inclusion of the `ProtoReflectionService` is optional, but it will
+allow us to easily query the monitoring service using the `grpc_cli` tool (see
+the next section).
+
+The next example demonstrates running a server with the monitoring service on a
+Unix domain socket (`/tmp/grpc_monitoring.sock`) alongside the
+`HelloWorldClient`. This will only work on `linux_x86_64` architectures.
+
+```diff
+--- a/examples/build.gradle
++++ b/examples/build.gradle
+@@ -27,7 +27,10 @@
+ dependencies {
+ compile "io.grpc:grpc-netty:${grpcVersion}"
+ compile "io.grpc:grpc-protobuf:${grpcVersion}"
++ compile "io.grpc:grpc-services:${grpcVersion}"
+ compile "io.grpc:grpc-stub:${grpcVersion}"
++ // This must match the version of netty used by grpcVersion
++ compile "io.netty:netty-transport-native-epoll:4.1.8.Final:linux-x86_64"
+
+ testCompile "junit:junit:4.11"
+ testCompile "org.mockito:mockito-core:1.9.5"
+--- a/examples/src/main/java/io/grpc/examples/helloworld/HelloWorldClient.java
++++ b/examples/src/main/java/io/grpc/examples/helloworld/HelloWorldClient.java
+@@ -33,7 +33,18 @@ package io.grpc.examples.helloworld;
+
+ import io.grpc.ManagedChannel;
+ import io.grpc.ManagedChannelBuilder;
++import io.grpc.Server;
+ import io.grpc.StatusRuntimeException;
++import io.grpc.netty.NettyServerBuilder;
++import io.grpc.protobuf.services.ProtoReflectionService;
++import io.grpc.services.MonitoringService;
++import io.netty.channel.EventLoopGroup;
++import io.netty.channel.epoll.EpollEventLoopGroup;
++import io.netty.channel.epoll.EpollServerDomainSocketChannel;
++import io.netty.channel.unix.DomainSocketAddress;
++import java.io.IOException;
++import java.nio.file.FileSystems;
++import java.nio.file.Files;
+ import java.util.concurrent.TimeUnit;
+ import java.util.logging.Level;
+ import java.util.logging.Logger;
+@@ -79,11 +90,50 @@ public class HelloWorldClient {
+ logger.info("Greeting: " + response.getMessage());
+ }
+
++ private static class MonitoringServer {
++ private Server server;
++ private EventLoopGroup bossEventLoopGroup;
++ private EventLoopGroup workerEventLoopGroup;
++
++ private void start(String udsFilename) throws IOException {
++ logger.info("Trying to start monitoring service on " + udsFilename);
++ logger.info("Will first delete " + udsFilename + " if it exists");
++ Files.deleteIfExists(FileSystems.getDefault().getPath(udsFilename));
++ bossEventLoopGroup = new EpollEventLoopGroup();
++ workerEventLoopGroup = new EpollEventLoopGroup();
++ /* This will throw an exception if StatsManager from instrumentation-java is unavailable */
++ server =
++ NettyServerBuilder.forAddress(new DomainSocketAddress(udsFilename))
++ .channelType(EpollServerDomainSocketChannel.class)
++ .bossEventLoopGroup(bossEventLoopGroup)
++ .workerEventLoopGroup(workerEventLoopGroup)
++ .addService(MonitoringService.getInstance())
++ .addService(ProtoReflectionService.newInstance())
++ .build();
++ server.start();
++ logger.info("Monitoring service started on " + udsFilename);
++ Runtime.getRuntime().addShutdownHook(new Thread() {
++ public void run() {
++ MonitoringServer.this.stop();
++ }
++ });
++ }
++
++ private void stop() {
++ server.shutdown();
++ bossEventLoopGroup.shutdownGracefully(0, 0, TimeUnit.SECONDS);
++ workerEventLoopGroup.shutdownGracefully(0, 0, TimeUnit.SECONDS);
++ }
++ }
++
+ /**
+ * Greet server. If provided, the first element of {@code args} is the name to use in the
+ * greeting.
+ */
+ public static void main(String[] args) throws Exception {
++ String udsFilename = "/tmp/grpc_monitoring.sock";
++ MonitoringServer monitoringServer = new MonitoringServer();
++ monitoringServer.start(udsFilename);
+ HelloWorldClient client = new HelloWorldClient("localhost", 50051);
+ try {
+ /* Access a service running on the local machine on port 50051 */
+@@ -92,8 +142,11 @@ public class HelloWorldClient {
+ user = args[0]; /* Use the arg as the name to greet if provided */
+ }
+ client.greet(user);
++ /* Sleep for 60 seconds to allow time for querying the monitoring server */
++ TimeUnit.SECONDS.sleep(60);
+ } finally {
+ client.shutdown();
++ monitoringServer.stop();
+ }
+ }
+ }
+```
+
+# Querying the service
+
+The monitoring service's `GetCanonicalStats` method returns a list of stats
+recorded by gRPC, including latencies, bytes sent and received, and error
+counts.
+
+You can query the service via the
+[`grpc_cli`](https://github.com/grpc/grpc/blob/master/doc/command_line_tool.md)
+command line tool as follows:
+
+```sh
+$ bins/opt/grpc_cli call localhost:50052 \
+ grpc.instrumentation.v1alpha.Monitoring.GetCanonicalRpcStats ''
+```
+
+If the server is running on a UDS, it can be queried as follows:
+
+```sh
+$ bins/opt/grpc_cli call 'unix:///tmp/grpc_monitoring.sock' \
+ grpc.instrumentation.v1alpha.Monitoring.GetCanonicalRpcStats ''
+```
+
+The response data comes as
+[`instrumentation`](https://github.com/google/instrumentation-proto/blob/master/stats/census.proto)
+protocol buffers. For each metric, such as `rpc_client_errors`, there is a
+`google.instrumentation.MeasurementDescriptor` and
+`google.instrumentation.ViewDescriptor` which describe the measurement and view,
+respectively, and a `google.instrumentation.View` that contains the actual data.
+The comments on the [proto
+file](https://github.com/google/instrumentation-proto/blob/master/stats/census.proto)
+describe the contents of each of these types.