Skip to content

Commit 625f65c

Browse files
YifeiZhuanglarry-safran
authored andcommitted
examples: add keepalive example (grpc#9956)
1 parent 972a4b0 commit 625f65c

File tree

5 files changed

+239
-0
lines changed

5 files changed

+239
-0
lines changed

examples/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ before trying out the examples.
119119

120120
</details>
121121

122+
- [Keep Alive](src/main/java/io/grpc/examples/keepalive)
123+
122124
### <a name="to-build-the-examples"></a> To build the examples
123125

124126
1. **[Install gRPC Java library SNAPSHOT locally, including code generation plugin](../COMPILING.md) (Only need this step for non-released versions, e.g. master HEAD).**

examples/build.gradle

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,19 +167,33 @@ task nameResolveClient(type: CreateStartScripts) {
167167
classpath = startScripts.classpath
168168
}
169169

170+
<<<<<<< HEAD
170171
task multiplexingServer(type: CreateStartScripts) {
171172
mainClass = 'io.grpc.examples.multiplex.MultiplexingServer'
172173
applicationName = 'multiplexing-server'
173174
outputDir = new File(project.buildDir, 'tmp/scripts/' + name)
174175
classpath = startScripts.classpath
175176
}
176177

178+
task keepAliveServer(type: CreateStartScripts) {
179+
mainClass = 'io.grpc.examples.keepalive.KeepAliveServer'
180+
applicationName = 'keep-alive-server'
181+
outputDir = new File(project.buildDir, 'tmp/scripts/' + name)
182+
classpath = startScripts.classpath
183+
}
184+
177185
task sharingClient(type: CreateStartScripts) {
178186
mainClass = 'io.grpc.examples.multiplex.SharingClient'
179187
applicationName = 'sharing-client'
180188
outputDir = new File(project.buildDir, 'tmp/scripts/' + name)
181189
classpath = startScripts.classpath
182190
}
191+
task keepAliveClient(type: CreateStartScripts) {
192+
mainClass = 'io.grpc.examples.keepalive.KeepAliveClient'
193+
applicationName = 'keep-alive-client'
194+
outputDir = new File(project.buildDir, 'tmp/scripts/' + name)
195+
classpath = startScripts.classpath
196+
}
183197

184198
applicationDistribution.into('bin') {
185199
from(routeGuideServer)
@@ -199,5 +213,7 @@ applicationDistribution.into('bin') {
199213
from(nameResolveClient)
200214
from(multiplexingServer)
201215
from(sharingClient)
216+
from(keepAliveServer)
217+
from(keepAliveClient)
202218
fileMode = 0755
203219
}

examples/logging.properties

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Create a file called logging.properties with the following contents.
2+
handlers=java.util.logging.ConsoleHandler
3+
io.grpc.level=FINE
4+
java.util.logging.ConsoleHandler.level=ALL
5+
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
6+
7+
# Pass the location of the file to JVM via this command-line flag
8+
JAVA_OPTS=-Djava.util.logging.config.file=logging.properties
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright 2023 The gRPC Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.grpc.examples.keepalive;
18+
19+
import io.grpc.Channel;
20+
import io.grpc.Grpc;
21+
import io.grpc.InsecureChannelCredentials;
22+
import io.grpc.ManagedChannel;
23+
import io.grpc.StatusRuntimeException;
24+
import io.grpc.examples.helloworld.GreeterGrpc;
25+
import io.grpc.examples.helloworld.HelloReply;
26+
import io.grpc.examples.helloworld.HelloRequest;
27+
import java.util.concurrent.TimeUnit;
28+
import java.util.logging.Level;
29+
import java.util.logging.Logger;
30+
31+
/**
32+
* A simple client that requests a greeting from the {@link KeepAliveServer}.
33+
*/
34+
public class KeepAliveClient {
35+
private static final Logger logger = Logger.getLogger(KeepAliveClient.class.getName());
36+
37+
private final GreeterGrpc.GreeterBlockingStub blockingStub;
38+
39+
/** Construct client for accessing HelloWorld server using the existing channel. */
40+
public KeepAliveClient(Channel channel) {
41+
// 'channel' here is a Channel, not a ManagedChannel, so it is not this code's responsibility to
42+
// shut it down.
43+
44+
// Passing Channels to code makes code easier to test and makes it easier to reuse Channels.
45+
blockingStub = GreeterGrpc.newBlockingStub(channel);
46+
}
47+
48+
/** Say hello to server. */
49+
public void greet(String name) {
50+
logger.info("Will try to greet " + name + " ...");
51+
HelloRequest request = HelloRequest.newBuilder().setName(name).build();
52+
HelloReply response;
53+
try {
54+
response = blockingStub.sayHello(request);
55+
} catch (StatusRuntimeException e) {
56+
logger.log(Level.WARNING, "RPC failed: {0}", e.getStatus());
57+
return;
58+
}
59+
logger.info("Greeting: " + response.getMessage());
60+
}
61+
62+
/**
63+
* Greet server.
64+
*/
65+
public static void main(String[] args) throws Exception {
66+
// Access a service running on the local machine on port 50051
67+
String target = "localhost:50051";
68+
69+
// Create a channel with the following keep alive configurations (demo only, you should set
70+
// more appropriate values based on your environment):
71+
// keepAliveTime: Send pings every 10 seconds if there is no activity. Set to an appropriate
72+
// value in reality, e.g. (5, TimeUnit.MINUTES).
73+
// keepAliveTimeout: Wait 1 second for ping ack before considering the connection dead. Set to a
74+
// larger value in reality, e.g. (10, TimeUnit.SECONDS). You should only set such a small value,
75+
// e.g. (1, TimeUnit.SECONDS) in certain low latency environments.
76+
// keepAliveWithoutCalls: Send pings even without active streams. Normally disable it.
77+
// Use JAVA_OPTS=-Djava.util.logging.config.file=logging.properties to see the keep alive ping
78+
// frames.
79+
// More details see: https://github.com/grpc/proposal/blob/master/A8-client-side-keepalive.md
80+
ManagedChannel channel = Grpc.newChannelBuilder(target, InsecureChannelCredentials.create())
81+
.keepAliveTime(5, TimeUnit.MINUTES)
82+
.keepAliveTime(10, TimeUnit.SECONDS) // Change to a larger value, e.g. 5min.
83+
.keepAliveTimeout(1, TimeUnit.SECONDS) // Change to a larger value, e.g. 10s.
84+
.keepAliveWithoutCalls(true)// You should normally avoid enabling this.
85+
.build();
86+
87+
try {
88+
KeepAliveClient client = new KeepAliveClient(channel);
89+
client.greet("Keep-alive Demo");
90+
Thread.sleep(30000);
91+
} finally {
92+
channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
93+
}
94+
}
95+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* Copyright 2023 The gRPC Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.grpc.examples.keepalive;
18+
19+
import io.grpc.Grpc;
20+
import io.grpc.InsecureServerCredentials;
21+
import io.grpc.Server;
22+
import io.grpc.examples.helloworld.GreeterGrpc;
23+
import io.grpc.examples.helloworld.HelloReply;
24+
import io.grpc.examples.helloworld.HelloRequest;
25+
import io.grpc.stub.StreamObserver;
26+
import java.io.IOException;
27+
import java.util.concurrent.TimeUnit;
28+
import java.util.logging.Logger;
29+
30+
/**
31+
* Server that manages startup/shutdown of a keep alive server.
32+
*/
33+
public class KeepAliveServer {
34+
private static final Logger logger = Logger.getLogger(KeepAliveServer.class.getName());
35+
36+
private Server server;
37+
38+
private void start() throws IOException {
39+
/* The port on which the server should run */
40+
int port = 50051;
41+
42+
// Start a server with the following configurations (demo only, you should set more appropriate
43+
// values based on your real environment):
44+
// keepAliveTime: Ping the client if it is idle for 5 seconds to ensure the connection is
45+
// still active. Set to an appropriate value in reality, e.g. in minutes.
46+
// keepAliveTimeout: Wait 1 second for the ping ack before assuming the connection is dead.
47+
// Set to an appropriate value in reality, e.g. (10, TimeUnit.SECONDS).
48+
// permitKeepAliveTime: If a client pings more than once every 5 seconds, terminate the
49+
// connection.
50+
// permitKeepAliveWithoutCalls: Allow pings even when there are no active streams.
51+
// maxConnectionIdle: If a client is idle for 15 seconds, send a GOAWAY.
52+
// maxConnectionAge: If any connection is alive for more than 30 seconds, send a GOAWAY.
53+
// maxConnectionAgeGrace: Allow 5 seconds for pending RPCs to complete before forcibly closing
54+
// connections.
55+
// Use JAVA_OPTS=-Djava.util.logging.config.file=logging.properties to see keep alive ping
56+
// frames.
57+
// More details see: https://github.com/grpc/proposal/blob/master/A9-server-side-conn-mgt.md
58+
server = Grpc.newServerBuilderForPort(port, InsecureServerCredentials.create())
59+
.addService(new GreeterImpl())
60+
.keepAliveTime(5, TimeUnit.SECONDS)
61+
.keepAliveTimeout(1, TimeUnit.SECONDS)
62+
.permitKeepAliveTime(5, TimeUnit.SECONDS)
63+
.permitKeepAliveWithoutCalls(true)
64+
.maxConnectionIdle(15, TimeUnit.SECONDS)
65+
.maxConnectionAge(30, TimeUnit.SECONDS)
66+
.maxConnectionAgeGrace(5, TimeUnit.SECONDS)
67+
.build()
68+
.start();
69+
logger.info("Server started, listening on " + port);
70+
Runtime.getRuntime().addShutdownHook(new Thread() {
71+
@Override
72+
public void run() {
73+
// Use stderr here since the logger may have been reset by its JVM shutdown hook.
74+
System.err.println("*** shutting down gRPC server since JVM is shutting down");
75+
try {
76+
KeepAliveServer.this.stop();
77+
} catch (InterruptedException e) {
78+
e.printStackTrace(System.err);
79+
}
80+
System.err.println("*** server shut down");
81+
}
82+
});
83+
}
84+
85+
private void stop() throws InterruptedException {
86+
if (server != null) {
87+
server.shutdown().awaitTermination(30, TimeUnit.SECONDS);
88+
}
89+
}
90+
91+
/**
92+
* Await termination on the main thread since the grpc library uses daemon threads.
93+
*/
94+
private void blockUntilShutdown() throws InterruptedException {
95+
if (server != null) {
96+
server.awaitTermination();
97+
}
98+
}
99+
100+
/**
101+
* Main launches the server from the command line.
102+
*/
103+
public static void main(String[] args) throws IOException, InterruptedException {
104+
final KeepAliveServer server = new KeepAliveServer();
105+
server.start();
106+
server.blockUntilShutdown();
107+
}
108+
109+
static class GreeterImpl extends GreeterGrpc.GreeterImplBase {
110+
111+
@Override
112+
public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
113+
HelloReply reply = HelloReply.newBuilder().setMessage("Hello " + req.getName()).build();
114+
responseObserver.onNext(reply);
115+
responseObserver.onCompleted();
116+
}
117+
}
118+
}

0 commit comments

Comments
 (0)