mirror of https://github.com/grpc/grpc-java.git
examples: Add an example for doing debug (#9957)
Extensive README, a server that exposes channelz and has pauses, and a client that uses multiple channels also exposes channelz service and has a 30 second delay to allow people to run the grpcdebug tool. Fixit b/259286633
This commit is contained in:
parent
1b799adc19
commit
78fff08eb1
|
@ -0,0 +1,239 @@
|
|||
# gRPC Debug Example
|
||||
|
||||
The debug example uses a Hello World style server whose response includes its
|
||||
hostname. It demonstrates usage of the AdminInterface and the grpcdebug
|
||||
commandline tool.
|
||||
|
||||
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
|
||||
|
||||
1. Optional: Build the hello-world-debug example client.
|
||||
See [the examples README](../README.md)
|
||||
|
||||
2. Build the debuggable server and client. From the
|
||||
`grpc-java/examples/examples-debug` directory run:
|
||||
|
||||
```bash
|
||||
$ ../gradlew installDist
|
||||
```
|
||||
|
||||
This creates the
|
||||
scripts `build/install/debug/bin/hostname-debuggable-server/bin/hostname-debuggable-server`
|
||||
that
|
||||
runs the example.
|
||||
|
||||
To run the debug example, run:
|
||||
|
||||
```bash
|
||||
$ ./build/install/debug/bin/hostname-debuggable-server/bin/hostname-debuggable-server
|
||||
```
|
||||
|
||||
And in a different terminal window run the client.
|
||||
|
||||
Note: You can use the standard hello-world client with no debugging enabled and
|
||||
still see results on the server. However, if you want to get debug information
|
||||
about the client you need to run the hello-world-debuggable client.
|
||||
|
||||
Simple client
|
||||
|
||||
```bash
|
||||
$ ../build/install/examples/bin/hello-world-client
|
||||
```
|
||||
|
||||
debug enabled client
|
||||
|
||||
```bash
|
||||
$ ./build/install/examples-debug/bin/hello-world-debuggable-client
|
||||
```
|
||||
|
||||
### Maven
|
||||
|
||||
If you prefer to use Maven:
|
||||
|
||||
1. Build the hello-world example client. See [the examples README](../README.md)
|
||||
|
||||
2. Run in this directory:
|
||||
|
||||
```bash
|
||||
$ mvn verify
|
||||
$ # Run the server (from the examples-debug directory)
|
||||
$ mvn exec:java -Dexec.mainClass=io.grpc.examples.debug.HostnameServer
|
||||
$ # In another terminal run the client (from the examples directory)
|
||||
$ cd ..
|
||||
$ mvn exec:java -Dexec.mainClass=io.grpc.examples.helloworld.HelloWorldClient
|
||||
```
|
||||
|
||||
## Using grpcdebug
|
||||
|
||||
grpcdebug is a tool that has been created to access the metrics from the
|
||||
channelz and health services.
|
||||
|
||||
### Installing the grpcdebug tool
|
||||
|
||||
The source code is located in a github project
|
||||
[grpc-ecosystem/grpcdebug](https://github.com/grpc-ecosystem/grpcdebug). You
|
||||
can either download [the latest built version]
|
||||
(https://github.com/grpc-ecosystem/grpcdebug/releases/latest) (recommended) or
|
||||
follow the README.md to build it yourself.
|
||||
|
||||
### Running the grpcdebug tool
|
||||
#### Usage
|
||||
`grpcdebug <target address> [flags] channelz <command> [argument]`
|
||||
|
||||
|
||||
| Command | Argument | Description |
|
||||
|:-----------|:--------------------:|:--------------------------------------------------|
|
||||
| channel | \<channel id or URL> | Display channel states in a human readable way. |
|
||||
| channels | | Lists client channels for the target application. |
|
||||
| server | \<server ID> | Displays server state in a human readable way. |
|
||||
| servers | | Lists servers in a human readable way. |
|
||||
| socket | \<socket ID> | Displays socket states in a human readable way. |
|
||||
| subchannel | \<id> | Display subchannel states in human readable way. |
|
||||
|
||||
Generally, you will start with either `servers` or `channels` and then work down
|
||||
to the details
|
||||
<br><br>
|
||||
|
||||
#### Getting overall server info
|
||||
```bash
|
||||
bin/grpcdebug/grpcdebug localhost:50051 channelz servers
|
||||
```
|
||||
This will show you the server ids with their activity
|
||||
```text
|
||||
Server ID Listen Addresses Calls(Started/Succeeded/Failed) Last Call Started
|
||||
2 [[::]:50051] 38/34/3 now
|
||||
```
|
||||
<br>
|
||||
|
||||
#### Getting details for a service
|
||||
```bash
|
||||
bin/grpcdebug/grpcdebug localhost:50051 channelz server 2
|
||||
```
|
||||
|
||||
The output will include more communication details and will show socket ids for
|
||||
currently connected clients
|
||||
|
||||
```text
|
||||
Server Id: 2
|
||||
Listen Addresses: [[::]:50051]
|
||||
Calls Started: 33
|
||||
Calls Succeeded: 29
|
||||
Calls Failed: 3
|
||||
Last Call Started: now
|
||||
---
|
||||
Socket ID Local->Remote Streams(Started/Succeeded/Failed) Messages(Sent/Received)
|
||||
19 [::1]:50051->[::1]:39834 4/3/0 3/4
|
||||
```
|
||||
|
||||
#### Displaying detailed info for a server side connection (socket)
|
||||
|
||||
```bash
|
||||
bin/grpcdebug/grpcdebug localhost:50051 channelz socket 19
|
||||
```
|
||||
|
||||
This will show a lot of gRPC internal information
|
||||
|
||||
```text
|
||||
Socket ID: 19
|
||||
Address: [::1]:50051->[::1]:50094
|
||||
Streams Started: 1
|
||||
Streams Succeeded: 0
|
||||
Streams Failed: 0
|
||||
Messages Sent: 0
|
||||
Messages Received: 1
|
||||
Keep Alives Sent: 0
|
||||
Last Local Stream Created:
|
||||
Last Remote Stream Created: now
|
||||
Last Message Sent Created:
|
||||
Last Message Received Created: now
|
||||
Local Flow Control Window: 65535
|
||||
Remote Flow Control Window: 1048569
|
||||
---
|
||||
Socket Options Name Value
|
||||
SO_LINGER [type.googleapis.com/grpc.channelz.v1.SocketOptionLinger]:{}
|
||||
io.grpc.netty.shaded.io.netty.channel.epoll.EpollChannelOption#TCP_CORK false
|
||||
WRITE_BUFFER_HIGH_WATER_MARK 65536
|
||||
WRITE_BUFFER_LOW_WATER_MARK 32768
|
||||
IP_TOS 0
|
||||
io.grpc.netty.shaded.io.netty.channel.epoll.EpollChannelOption#TCP_KEEPCNT 9
|
||||
SINGLE_EVENTEXECUTOR_PER_GROUP true
|
||||
SO_SNDBUF 2626560
|
||||
io.grpc.netty.shaded.io.netty.channel.epoll.EpollChannelOption#TCP_NOTSENT_LOWAT 0
|
||||
WRITE_BUFFER_WATER_MARK WriteBufferWaterMark(low: 32768, high: 65536)
|
||||
TCP_NODELAY true
|
||||
SO_RCVBUF 131072
|
||||
io.grpc.netty.shaded.io.netty.channel.epoll.EpollChannelOption#SO_BUSY_POLL 0
|
||||
IP_TRANSPARENT false
|
||||
SO_KEEPALIVE true
|
||||
io.grpc.netty.shaded.io.netty.channel.epoll.EpollChannelOption#TCP_QUICKACK false
|
||||
ALLOCATOR PooledByteBufAllocator(directByDefault: true)
|
||||
TCP_FASTOPEN_CONNECT false
|
||||
MESSAGE_SIZE_ESTIMATOR io.grpc.netty.shaded.io.netty.channel.DefaultMessageSizeEstimator@48d475b6
|
||||
WRITE_SPIN_COUNT 16
|
||||
SO_REUSEADDR true
|
||||
CONNECT_TIMEOUT_MILLIS 30000
|
||||
ALLOW_HALF_CLOSURE false
|
||||
io.grpc.netty.shaded.io.netty.channel.epoll.EpollChannelOption#EPOLL_MODE EDGE_TRIGGERED
|
||||
MAX_MESSAGES_PER_READ 16
|
||||
io.grpc.netty.shaded.io.netty.channel.epoll.EpollChannelOption#TCP_KEEPIDLE 7200
|
||||
AUTO_CLOSE true
|
||||
io.grpc.netty.shaded.io.netty.channel.epoll.EpollChannelOption#TCP_KEEPINTVL 75
|
||||
MAX_MESSAGES_PER_WRITE 2147483647
|
||||
AUTO_READ true
|
||||
TCP_MD5SIG null
|
||||
RCVBUF_ALLOCATOR io.grpc.netty.shaded.io.netty.channel.AdaptiveRecvByteBufAllocator@360691a0
|
||||
```
|
||||
#### Displaying the list of gRPC client channels
|
||||
Command
|
||||
```bash
|
||||
bin/grpcdebug/grpcdebug localhost:50051 channelz channels
|
||||
```
|
||||
Output
|
||||
```text
|
||||
Channel ID Target State Calls(Started/Succeeded/Failed) Created Time
|
||||
1 localhost:50051 READY 34/34/0
|
||||
3 localhost:50051 READY 16/16/0
|
||||
```
|
||||
Note: If you have a simple server that doesn't use gRPC clients to contact other
|
||||
servers, then this table will be empty.
|
||||
|
||||
#### Displaying details of a gRPC client channel
|
||||
Command
|
||||
```bash
|
||||
bin/grpcdebug/grpcdebug localhost:50051 channelz channel 3
|
||||
```
|
||||
Output
|
||||
```text
|
||||
Channel ID: 3
|
||||
Target: localhost:50051
|
||||
State: READY
|
||||
Calls Started: 16
|
||||
Calls Succeeded: 16
|
||||
Calls Failed: 0
|
||||
Created Time:
|
||||
---
|
||||
Subchannel ID Target State Calls(Started/Succeeded/Failed) CreatedTime
|
||||
10 [[[localhost/127.0.0.1:50051]/{}], [[localhost/0:0 READY 16/16/0
|
||||
```
|
||||
|
||||
#### Displaying details of a gRPC client subchannel
|
||||
Command
|
||||
```bash
|
||||
bin/grpcdebug/grpcdebug localhost:50051 channelz subchannel 10
|
||||
```
|
||||
Output
|
||||
```text
|
||||
Subchannel ID: 10
|
||||
Target: [[[localhost/127.0.0.1:50051]/{}], [[localhost/0:0:0:0:0:0:0:1:50051]/{}]]
|
||||
State: READY
|
||||
Calls Started: 16
|
||||
Calls Succeeded: 16
|
||||
Calls Failed: 0
|
||||
Created Time:
|
||||
---
|
||||
Socket ID Local->Remote Streams(Started/Succeeded/Failed) Messages(Sent/Received)
|
||||
11 127.0.0.1:48536->127.0.0.1:50051 16/16/0 12/12
|
||||
```
|
|
@ -0,0 +1,76 @@
|
|||
plugins {
|
||||
id 'application' // Provide convenience executables for trying out the examples.
|
||||
id 'java'
|
||||
|
||||
id "com.google.protobuf" version "0.8.17"
|
||||
|
||||
// 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()
|
||||
}
|
||||
|
||||
sourceCompatibility = 1.8
|
||||
targetCompatibility = 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.55.0-SNAPSHOT' // CURRENT_GRPC_VERSION
|
||||
def protobufVersion = '3.21.7'
|
||||
|
||||
dependencies {
|
||||
implementation "io.grpc:grpc-protobuf:${grpcVersion}"
|
||||
implementation "io.grpc:grpc-stub:${grpcVersion}"
|
||||
implementation "io.grpc:grpc-services:${grpcVersion}"
|
||||
compileOnly "org.apache.tomcat:annotations-api:6.0.53"
|
||||
runtimeOnly "io.grpc:grpc-netty-shaded:${grpcVersion}"
|
||||
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
testImplementation "io.grpc:grpc-testing:${grpcVersion}"
|
||||
}
|
||||
|
||||
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 HelloWorldDebuggableClient(type: CreateStartScripts) {
|
||||
mainClass = 'io.grpc.examples.debug.HelloWorldDebuggableClient'
|
||||
applicationName = 'hello-world-debuggable-client'
|
||||
outputDir = new File(project.buildDir, 'tmp/scripts/' + name)
|
||||
classpath = startScripts.classpath
|
||||
}
|
||||
|
||||
task HostnameDebuggableServer(type: CreateStartScripts) {
|
||||
mainClass = 'io.grpc.examples.debug.HostnameDebuggableServer'
|
||||
applicationName = 'hostname-debuggable-server'
|
||||
outputDir = new File(project.buildDir, 'tmp/scripts/' + name)
|
||||
classpath = startScripts.classpath
|
||||
}
|
||||
|
||||
applicationDistribution.into('bin') {
|
||||
from(HelloWorldDebuggableClient)
|
||||
from(HostnameDebuggableServer)
|
||||
fileMode = 0755
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
<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-debug</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.55.0-SNAPSHOT</version><!-- CURRENT_GRPC_VERSION -->
|
||||
<name>example-debug</name>
|
||||
<url>https://github.com/grpc/grpc-java</url>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<grpc.version>1.55.0-SNAPSHOT</grpc.version><!-- CURRENT_GRPC_VERSION -->
|
||||
<protoc.version>3.21.7</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-protobuf</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-stub</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.grpc</groupId>
|
||||
<artifactId>grpc-services</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 @@
|
|||
rootProject.name = 'example-debug'
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* Copyright 2023 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.debug;
|
||||
|
||||
import io.grpc.Channel;
|
||||
import io.grpc.Grpc;
|
||||
import io.grpc.InsecureChannelCredentials;
|
||||
import io.grpc.InsecureServerCredentials;
|
||||
import io.grpc.ManagedChannel;
|
||||
import io.grpc.Server;
|
||||
import io.grpc.ServerBuilder;
|
||||
import io.grpc.StatusRuntimeException;
|
||||
import io.grpc.examples.helloworld.GreeterGrpc;
|
||||
import io.grpc.examples.helloworld.HelloReply;
|
||||
import io.grpc.examples.helloworld.HelloRequest;
|
||||
import io.grpc.protobuf.services.ProtoReflectionService;
|
||||
import io.grpc.services.AdminInterface;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* A client that creates a channelz service and then requests a greeting 50 times.
|
||||
* It uses 2 channels to communicate with the server one of which is shared by 2 stubs and
|
||||
* one of which has only 1 stub. The requests are split over the 3 channels.
|
||||
* Once completed, there is a 30 second sleep to allow more time to run the commandline debugger.
|
||||
*/
|
||||
public class HelloWorldDebuggableClient {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(HelloWorldDebuggableClient.class.getName());
|
||||
public static final int NUM_ITERATIONS = 50;
|
||||
|
||||
private final GreeterGrpc.GreeterBlockingStub blockingStub;
|
||||
|
||||
/** Construct client for accessing HelloWorld server using the specified channel. */
|
||||
public HelloWorldDebuggableClient(Channel channel) {
|
||||
blockingStub = GreeterGrpc.newBlockingStub(channel);
|
||||
}
|
||||
|
||||
/** Say hello to server. */
|
||||
public void greet(String name) {
|
||||
logger.info("Will try to greet " + 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());
|
||||
}
|
||||
|
||||
/**
|
||||
* Greet server. If provided, the first element of {@code args} is the name to use in the
|
||||
* 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
|
||||
String target = "localhost:50051";
|
||||
int debugPort = 51051;
|
||||
// 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 pair of communication channels to the server. Channels are thread-safe
|
||||
// and reusable.
|
||||
ManagedChannel channel1 = Grpc.newChannelBuilder(target,
|
||||
InsecureChannelCredentials.create()).build();
|
||||
ManagedChannel channel2 = Grpc.newChannelBuilder(target,
|
||||
InsecureChannelCredentials.create()).build();
|
||||
Server server = null;
|
||||
try {
|
||||
// Create a service from which grpcdebug can request debug info
|
||||
server = Grpc.newServerBuilderForPort(debugPort, InsecureServerCredentials.create())
|
||||
.addServices(AdminInterface.getStandardServices())
|
||||
.build()
|
||||
.start();
|
||||
|
||||
// Create the 3 clients
|
||||
HelloWorldDebuggableClient client1 = new HelloWorldDebuggableClient(channel1);
|
||||
HelloWorldDebuggableClient client2 = new HelloWorldDebuggableClient(channel1);
|
||||
HelloWorldDebuggableClient client3 = new HelloWorldDebuggableClient(channel2);
|
||||
|
||||
// Do the client requests spreadying them over the 3 clients
|
||||
for (int i=0; i < NUM_ITERATIONS; i++) {
|
||||
switch (i % 3) {
|
||||
case 0:
|
||||
client1.greet(user);
|
||||
break;
|
||||
case 1:
|
||||
client2.greet(user);
|
||||
break;
|
||||
case 2:
|
||||
client3.greet(user);
|
||||
break;
|
||||
}
|
||||
}
|
||||
System.out.println("Completed " + NUM_ITERATIONS +
|
||||
" requests, will now sleep for 30 seconds to give some time for command line calls");
|
||||
Thread.sleep(30000); // Give some time for running grpcdebug
|
||||
} finally {
|
||||
// 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.
|
||||
channel1.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
|
||||
channel2.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
|
||||
|
||||
if (server != null) {
|
||||
server.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright 2023 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.debug;
|
||||
|
||||
import io.grpc.Grpc;
|
||||
import io.grpc.InsecureServerCredentials;
|
||||
import io.grpc.Server;
|
||||
import io.grpc.ServerBuilder;
|
||||
import io.grpc.health.v1.HealthCheckResponse.ServingStatus;
|
||||
import io.grpc.protobuf.services.ProtoReflectionService;
|
||||
import io.grpc.services.AdminInterface;
|
||||
import io.grpc.services.HealthStatusManager;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* A server that hosts HostnameGreeter, plus the channelz service which grpcdebug uses.
|
||||
*/
|
||||
public final class HostnameDebuggableServer {
|
||||
static int port = 50051;
|
||||
static String hostname = null;
|
||||
|
||||
public static void main(String[] args) throws IOException, InterruptedException {
|
||||
parseArgs(args); // sets port and hostname
|
||||
|
||||
final Server server = Grpc.newServerBuilderForPort(port, InsecureServerCredentials.create())
|
||||
.addService(new HostnameGreeter(hostname))
|
||||
.addServices(AdminInterface.getStandardServices()) // the key add for enabling grpcdebug
|
||||
.build()
|
||||
.start();
|
||||
|
||||
System.out.println("Listening on port " + port);
|
||||
|
||||
addShutdownHook(server); // Configures cleanup
|
||||
server.awaitTermination(); // Block until shutdown
|
||||
}
|
||||
|
||||
private static void parseArgs(String[] args) {
|
||||
if (args.length >= 1) {
|
||||
try {
|
||||
port = Integer.parseInt(args[0]);
|
||||
} catch (NumberFormatException ex) {
|
||||
System.err.println("Usage: [port [hostname]]");
|
||||
System.err.println("");
|
||||
System.err.println(" port The listen port. Defaults to " + port);
|
||||
System.err.println(" hostname The name clients will see in greet responses. ");
|
||||
System.err.println(" Defaults to the machine's hostname");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
if (args.length >= 2) {
|
||||
hostname = args[1];
|
||||
}
|
||||
}
|
||||
|
||||
private static void addShutdownHook(final Server server) {
|
||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Start graceful shutdown
|
||||
server.shutdown();
|
||||
try {
|
||||
// Wait for RPCs to complete processing
|
||||
if (!server.awaitTermination(30, TimeUnit.SECONDS)) {
|
||||
// That was plenty of time. Let's cancel the remaining RPCs
|
||||
server.shutdownNow();
|
||||
// shutdownNow isn't instantaneous, so give a bit of time to clean resources up
|
||||
// gracefully. Normally this will be well under a second.
|
||||
server.awaitTermination(5, TimeUnit.SECONDS);
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
server.shutdownNow();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright 2023 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.debug;
|
||||
|
||||
import io.grpc.examples.helloworld.GreeterGrpc;
|
||||
import io.grpc.examples.helloworld.HelloReply;
|
||||
import io.grpc.examples.helloworld.HelloRequest;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/** Greeter implementation which replies identifying itself with its hostname. */
|
||||
public final class HostnameGreeter extends GreeterGrpc.GreeterImplBase {
|
||||
private static final Logger logger = Logger.getLogger(HostnameGreeter.class.getName());
|
||||
|
||||
private AtomicInteger callCount = new AtomicInteger();
|
||||
|
||||
private final String serverName;
|
||||
|
||||
public HostnameGreeter(String serverName) {
|
||||
if (serverName == null) {
|
||||
serverName = determineHostname();
|
||||
}
|
||||
this.serverName = serverName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
|
||||
int curCount = callCount.incrementAndGet();
|
||||
HelloReply reply = HelloReply.newBuilder()
|
||||
.setMessage(String.format("Hello %s, from %s. You are requester number %d.",
|
||||
req.getName(), serverName, curCount))
|
||||
.build();
|
||||
// Add a pause so that there is time to run debug commands
|
||||
try {
|
||||
int sleep_interval = (curCount % 10) * 100; // 0 - 1 second
|
||||
Thread.sleep(sleep_interval);
|
||||
} catch (InterruptedException e) {
|
||||
responseObserver.onError(e);
|
||||
}
|
||||
// Send the response
|
||||
responseObserver.onNext(reply);
|
||||
responseObserver.onCompleted();
|
||||
}
|
||||
|
||||
private static String determineHostname() {
|
||||
try {
|
||||
return InetAddress.getLocalHost().getHostName();
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.INFO, "Failed to determine hostname. Will generate one", ex);
|
||||
}
|
||||
// Strange. Well, let's make an identifier for ourselves.
|
||||
return "generated-" + new Random().nextInt();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue