Skip to content

Commit 3ed732f

Browse files
authored
examples: Clean up Health, and document need for grpc-services
For something like this, putting all the config together in service config is what we would want to encourage. Remove unused/needless variables. Emphasize health checking more than round-robin. Add missing synchronization on server-side.
1 parent eac9fe9 commit 3ed732f

4 files changed

Lines changed: 42 additions & 38 deletions

File tree

β€Žexamples/build.gradleβ€Ž

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ def protocVersion = protobufVersion
2727

2828
dependencies {
2929
implementation "io.grpc:grpc-protobuf:${grpcVersion}"
30+
// Even though client-side won't call grpc-services directly, it needs the
31+
// dependency to enable the health-aware round_robin implementation
3032
implementation "io.grpc:grpc-services:${grpcVersion}"
3133
implementation "io.grpc:grpc-stub:${grpcVersion}"
3234

β€Žexamples/src/main/java/io/grpc/examples/healthservice/HealthServiceClient.javaβ€Ž

Lines changed: 32 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import io.grpc.health.v1.HealthCheckResponse.ServingStatus;
3333
import io.grpc.health.v1.HealthGrpc;
3434
import java.util.Arrays;
35+
import java.util.Collections;
3536
import java.util.HashMap;
3637
import java.util.Map;
3738
import java.util.concurrent.TimeUnit;
@@ -45,25 +46,17 @@ public class HealthServiceClient {
4546
private static final Logger logger = Logger.getLogger(HealthServiceClient.class.getName());
4647

4748
private final GreeterGrpc.GreeterBlockingStub greeterBlockingStub;
48-
private final HealthGrpc.HealthStub healthStub;
4949
private final HealthGrpc.HealthBlockingStub healthBlockingStub;
5050

51-
private final HealthCheckRequest healthRequest;
52-
5351
/** Construct client for accessing HelloWorld server using the existing channel. */
5452
public HealthServiceClient(Channel channel) {
5553
greeterBlockingStub = GreeterGrpc.newBlockingStub(channel);
56-
healthStub = HealthGrpc.newStub(channel);
5754
healthBlockingStub = HealthGrpc.newBlockingStub(channel);
58-
healthRequest = HealthCheckRequest.getDefaultInstance();
59-
LoadBalancerProvider roundRobin = LoadBalancerRegistry.getDefaultRegistry()
60-
.getProvider("round_robin");
61-
6255
}
6356

6457
private ServingStatus checkHealth(String prefix) {
6558
HealthCheckResponse response =
66-
healthBlockingStub.check(healthRequest);
59+
healthBlockingStub.check(HealthCheckRequest.getDefaultInstance());
6760
logger.info(prefix + ", current health is: " + response.getStatus());
6861
return response.getStatus();
6962
}
@@ -86,34 +79,35 @@ public void greet(String name) {
8679
}
8780

8881

89-
private static void runTest(String target, String[] users, boolean useRoundRobin)
82+
private static void runTest(String target, String[] users, boolean enableHealthChecking)
9083
throws InterruptedException {
91-
ManagedChannelBuilder<?> builder =
92-
Grpc.newChannelBuilder(target, InsecureChannelCredentials.create());
93-
94-
// Round Robin, when a healthCheckConfig is present in the default service configuration, runs
95-
// a watch on the health service and when picking an endpoint will
96-
// consider a transport to a server whose service is not in SERVING state to be unavailable.
97-
// Since we only have a single server we are connecting to, then the load balancer will
98-
// return an error without sending the RPC.
99-
if (useRoundRobin) {
100-
builder = builder
101-
.defaultLoadBalancingPolicy("round_robin")
102-
.defaultServiceConfig(generateHealthConfig(""));
84+
String healthServiceName;
85+
if (enableHealthChecking) {
86+
healthServiceName = ""; // requests the backend's "overall health status"
87+
} else {
88+
healthServiceName = null; // disables health checking in generateServiceConfig()
10389
}
90+
ManagedChannel channel =
91+
Grpc.newChannelBuilder(target, InsecureChannelCredentials.create())
92+
// Enable the round_robin load balancer, with or without health checking
93+
.defaultServiceConfig(generateServiceConfig(healthServiceName))
94+
.build();
10495

105-
ManagedChannel channel = builder.build();
96+
// Round Robin, when a healthCheckConfig is present in the service configuration, runs a watch
97+
// on the health service and when picking an endpoint will consider a transport to a server
98+
// whose service is not in SERVING state to be unavailable. Since we only have a single server
99+
// we are connecting to, then the load balancer will return an error without sending the RPC.
106100

107-
System.out.println("\nDoing test with" + (useRoundRobin ? "" : "out")
108-
+ " the Round Robin load balancer\n");
101+
System.out.println("\nDoing test with" + (enableHealthChecking ? "" : "out")
102+
+ " health checking\n");
109103

110104
try {
111105
HealthServiceClient client = new HealthServiceClient(channel);
112-
if (!useRoundRobin) {
106+
if (!enableHealthChecking) {
113107
client.checkHealth("Before call");
114108
}
115109
client.greet(users[0]);
116-
if (!useRoundRobin) {
110+
if (!enableHealthChecking) {
117111
client.checkHealth("After user " + users[0]);
118112
}
119113

@@ -122,7 +116,7 @@ private static void runTest(String target, String[] users, boolean useRoundRobin
122116
Thread.sleep(100); // Since the health update is asynchronous give it time to propagate
123117
}
124118

125-
if (!useRoundRobin) {
119+
if (!enableHealthChecking) {
126120
client.checkHealth("After all users");
127121
Thread.sleep(10000);
128122
client.checkHealth("After 10 second wait");
@@ -137,12 +131,17 @@ private static void runTest(String target, String[] users, boolean useRoundRobin
137131
channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS);
138132
}
139133
}
140-
private static Map<String, Object> generateHealthConfig(String serviceName) {
134+
private static Map<String, Object> generateServiceConfig(String healthServiceName) {
141135
Map<String, Object> config = new HashMap<>();
142-
Map<String, Object> serviceMap = new HashMap<>();
143-
144-
config.put("healthCheckConfig", serviceMap);
145-
serviceMap.put("serviceName", serviceName);
136+
if (healthServiceName != null) {
137+
config.put("healthCheckConfig", Collections.singletonMap("serviceName", healthServiceName));
138+
}
139+
// There is more than one round_robin implementation. If the client doesn't depend on
140+
// io.grpc:grpc-services, then the round_robin implementation does not support health watching
141+
// (to avoid a Protobuf dependency). When the client depends on grpc-services the
142+
// health-supporting round_robin implementation is used instead.
143+
config.put("loadBalancingConfig", Arrays.asList(
144+
Collections.singletonMap("round_robin", Collections.emptyMap())));
146145
return config;
147146
}
148147

β€Žexamples/src/main/java/io/grpc/examples/healthservice/HealthServiceServer.javaβ€Ž

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public static void main(String[] args) throws IOException, InterruptedException
9494
}
9595

9696
private class GreeterImpl extends GreeterGrpc.GreeterImplBase {
97-
boolean isServing = true;
97+
private volatile boolean isServing = true;
9898

9999
@Override
100100
public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
@@ -134,7 +134,7 @@ public void run() {
134134
}
135135

136136
private boolean isNameLongEnough(HelloRequest req) {
137-
return isServing && req.getName().length() >= 5;
137+
return req.getName().length() >= 5;
138138
}
139139
}
140140
}
Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
gRPC Health Service Example
22
=====================
33

4-
The Health Service example provides a HelloWorld gRPC server that doesn't like short names along with a
5-
health service. It also provides a client application which makes HelloWorld
6-
calls and checks the health status.
4+
The Health Service example provides a HelloWorld gRPC server that doesn't like
5+
short names along with a health service. It also provides a client application
6+
which makes HelloWorld calls and checks the health status.
77

88
The client application also shows how the round robin load balancer can
99
utilize the health status to avoid making calls to a service that is
1010
not actively serving.
11+
12+
Note that clients must depend on `io.grpc:grpc-services` for the health-aware
13+
round_robin implementation to be used.

0 commit comments

Comments
 (0)