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:
Larry Safran 2023-03-17 16:26:06 -07:00 committed by GitHub
parent 1b799adc19
commit 78fff08eb1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 775 additions and 0 deletions

View File

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

View File

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

View File

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

View File

@ -0,0 +1 @@
rootProject.name = 'example-debug'

View File

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

View File

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

View File

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

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