Skip to content

Commit abf7fb8

Browse files
authored
Merge pull request #1103 from charlesmst/actuator-healthcheck
Actuator healthcheck
2 parents 48073ab + 13ece45 commit abf7fb8

File tree

10 files changed

+455
-3
lines changed

10 files changed

+455
-3
lines changed

docs/en/actuator.md

+25-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ This page focuses on the integration with
77
This is an optional feature. Supported features:
88

99
- Client + server metrics
10-
- Server `InfoContributor`
10+
- Server `InfoContributor` and `GRPC Health API`
1111

1212
## Table of Contents <!-- omit in toc -->
1313

@@ -18,6 +18,7 @@ This is an optional feature. Supported features:
1818
- [Viewing the metrics](#viewing-the-metrics)
1919
- [Metric configuration](#metric-configuration)
2020
- [InfoContributor](#infocontributor)
21+
- [GRPC Health](#grpc-health)
2122
- [Opt-Out](#opt-out)
2223

2324
## Dependencies
@@ -178,6 +179,29 @@ You can view the grpc info along with your other info at `/actuator/info` (requi
178179
179180
You can turn of the service listing (for both actuator and grpc) using `grpc.server.reflectionServiceEnabled=false`.
180181
182+
## GRPC Health
183+
184+
By default, the health endpoint will use the standard gRPC implementation for health, which does not integrate with Spring Boot Actuator.
185+
186+
The server provides an optional integration with Actuator health information using the [gRPC Health API](https://grpc.io/docs/guides/health-checking/).
187+
188+
This integration enables the server to respond to gRPC health checks based on the `HealthEndpoint` from Actuator, which is the same used for the web version.
189+
190+
To enable this integration, add the following properties to your application configuration:
191+
192+
````properties
193+
grpc.server.health-service.type=ACTUATOR
194+
````
195+
196+
The integration allows you to check the health status for the whole service or specific health indicators, where the `service` is the key of the [`HealthIndicator`](https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.endpoints.health.auto-configured-health-indicators).
197+
`Watch` is not yet supported because actuator is pull-based and does not automatically tries to determine the status of the service to notify clients.
198+
199+
To prevent any health service from being served by the GRPC server, you can set the type to `NONE`:
200+
201+
````properties
202+
grpc.server.health-service.type=NONE
203+
````
204+
181205
## Opt-Out
182206

183207
You can opt out from the actuator autoconfiguration using the following annotation:

grpc-server-spring-boot-starter/src/main/java/net/devh/boot/grpc/server/autoconfigure/GrpcHealthServiceAutoConfiguration.java

+15
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
package net.devh.boot.grpc.server.autoconfigure;
1818

19+
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
20+
import org.springframework.boot.actuate.health.HealthEndpoint;
21+
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
1922
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
2023
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2124
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -25,6 +28,7 @@
2528

2629
import io.grpc.BindableService;
2730
import io.grpc.protobuf.services.HealthStatusManager;
31+
import net.devh.boot.grpc.server.health.ActuatorGrpcHealth;
2832
import net.devh.boot.grpc.server.service.GrpcService;
2933

3034
/**
@@ -36,6 +40,7 @@
3640
@ConditionalOnClass(HealthStatusManager.class)
3741
@ConditionalOnProperty(prefix = "grpc.server", name = "health-service-enabled", matchIfMissing = true)
3842
@AutoConfigureBefore(GrpcServerFactoryAutoConfiguration.class)
43+
@AutoConfigureAfter(HealthEndpointAutoConfiguration.class)
3944
public class GrpcHealthServiceAutoConfiguration {
4045

4146
/**
@@ -45,14 +50,24 @@ public class GrpcHealthServiceAutoConfiguration {
4550
*/
4651
@Bean
4752
@ConditionalOnMissingBean
53+
@ConditionalOnProperty(prefix = "grpc.server", name = "health-service.type", havingValue = "GRPC",
54+
matchIfMissing = true)
4855
HealthStatusManager healthStatusManager() {
4956
return new HealthStatusManager();
5057
}
5158

5259
@Bean
5360
@GrpcService
61+
@ConditionalOnProperty(prefix = "grpc.server", name = "health-service.type", havingValue = "GRPC",
62+
matchIfMissing = true)
5463
BindableService grpcHealthService(final HealthStatusManager healthStatusManager) {
5564
return healthStatusManager.getHealthService();
5665
}
5766

67+
@Bean
68+
@GrpcService
69+
@ConditionalOnProperty(prefix = "grpc.server", name = "health-service.type", havingValue = "ACTUATOR")
70+
BindableService grpcHealthServiceActuator(final HealthEndpoint healthStatusManager) {
71+
return new ActuatorGrpcHealth(healthStatusManager);
72+
}
5873
}

grpc-server-spring-boot-starter/src/main/java/net/devh/boot/grpc/server/config/GrpcServerProperties.java

+10
Original file line numberDiff line numberDiff line change
@@ -223,11 +223,21 @@ public class GrpcServerProperties {
223223
/**
224224
* Whether gRPC health service is enabled or not. Defaults to {@code true}.
225225
*
226+
* @deprecated Use {@link HealthOptions#type health type} @{@link HealthType#NONE NONE} instead.
226227
* @param healthServiceEnabled Whether gRPC health service is enabled.
227228
* @return True, if the health service is enabled. False otherwise.
228229
*/
230+
@Deprecated
229231
private boolean healthServiceEnabled = true;
230232

233+
/**
234+
* Implementation of gRPC health service. Defaults the type to {@link HealthType#GRPC GRPC}.
235+
*
236+
* @param healthService The options for the health service.
237+
* @return The type of health service to use.
238+
*/
239+
private HealthOptions healthService = new HealthOptions();
240+
231241
/**
232242
* Whether proto reflection service is enabled or not. Defaults to {@code true}.
233243
*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright (c) 2016-2024 The gRPC-Spring 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 net.devh.boot.grpc.server.config;
18+
19+
import lombok.Data;
20+
21+
/**
22+
* GRPC Health service options.
23+
*/
24+
@Data
25+
public class HealthOptions {
26+
27+
/**
28+
* Implementation of gRPC health service. Defaults to {@link HealthType#GRPC GRPC}. To disable health service set to
29+
* {@link HealthType#NONE NONE}.
30+
*
31+
* @see net.devh.boot.grpc.server.autoconfigure.GrpcHealthServiceAutoConfiguration
32+
* @param type The implementation of gRPC health service.
33+
* @return GRPC, ACTUATOR or NONE.
34+
*/
35+
private HealthType type = HealthType.GRPC;
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright (c) 2016-2024 The gRPC-Spring 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 net.devh.boot.grpc.server.config;
18+
19+
20+
/**
21+
* Enum to specify the type of health service to use in GRPC.
22+
*/
23+
public enum HealthType {
24+
/**
25+
* Use the standard GRPC health service from io.grpc.
26+
*
27+
* @see net.devh.boot.grpc.server.autoconfigure.GrpcHealthServiceAutoConfiguration#grpcHealthService
28+
*/
29+
GRPC,
30+
/**
31+
* Uses a bridge to the Spring Boot Actuator health service.
32+
*
33+
* @see net.devh.boot.grpc.server.autoconfigure.GrpcHealthServiceAutoConfiguration#grpcHealthServiceActuator
34+
* @see net.devh.boot.grpc.server.health.ActuatorGrpcHealth
35+
*/
36+
ACTUATOR,
37+
/**
38+
* No health service will be created.
39+
*/
40+
NONE
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright (c) 2016-2024 The gRPC-Spring 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 net.devh.boot.grpc.server.health;
18+
19+
20+
import static io.grpc.Status.NOT_FOUND;
21+
22+
import org.springframework.boot.actuate.health.HealthComponent;
23+
import org.springframework.boot.actuate.health.HealthEndpoint;
24+
import org.springframework.boot.actuate.health.Status;
25+
26+
import io.grpc.StatusException;
27+
import io.grpc.health.v1.HealthCheckRequest;
28+
import io.grpc.health.v1.HealthCheckResponse;
29+
import io.grpc.health.v1.HealthGrpc;
30+
import io.grpc.stub.StreamObserver;
31+
32+
public class ActuatorGrpcHealth extends HealthGrpc.HealthImplBase {
33+
private final HealthEndpoint healthEndpoint;
34+
35+
public ActuatorGrpcHealth(HealthEndpoint healthEndpoint) {
36+
this.healthEndpoint = healthEndpoint;
37+
}
38+
39+
public void check(HealthCheckRequest request, StreamObserver<HealthCheckResponse> responseObserver) {
40+
41+
if (!request.getService().isEmpty()) {
42+
HealthComponent health = healthEndpoint.healthForPath(request.getService());
43+
if (health == null) {
44+
responseObserver.onError(
45+
new StatusException(NOT_FOUND.withDescription("unknown service " + request.getService())));
46+
return;
47+
}
48+
Status status = health.getStatus();
49+
HealthCheckResponse.ServingStatus result = resolveStatus(status);
50+
HealthCheckResponse response = HealthCheckResponse.newBuilder().setStatus(result).build();
51+
responseObserver.onNext(response);
52+
responseObserver.onCompleted();
53+
} else {
54+
Status status = healthEndpoint.health().getStatus();
55+
HealthCheckResponse.ServingStatus result = resolveStatus(status);
56+
HealthCheckResponse response = HealthCheckResponse.newBuilder().setStatus(result).build();
57+
responseObserver.onNext(response);
58+
responseObserver.onCompleted();
59+
}
60+
61+
}
62+
63+
private HealthCheckResponse.ServingStatus resolveStatus(Status status) {
64+
if (Status.UP.equals(status)) {
65+
return HealthCheckResponse.ServingStatus.SERVING;
66+
}
67+
if (Status.DOWN.equals(status) || Status.OUT_OF_SERVICE.equals(status)) {
68+
return HealthCheckResponse.ServingStatus.NOT_SERVING;
69+
}
70+
return HealthCheckResponse.ServingStatus.UNKNOWN;
71+
}
72+
}

grpc-server-spring-boot-starter/src/test/java/net/devh/boot/grpc/server/autoconfigure/GrpcHealthServiceFalseAutoConfigurationTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
import io.grpc.health.v1.HealthCheckResponse;
3131

3232
@SpringBootTest(classes = GrpcHealthServiceDefaultAutoConfigurationTest.TestConfig.class,
33-
properties = "grpc.server.health-service-enabled=false")
33+
properties = "grpc.server.health-service.type=NONE")
3434
@ImportAutoConfiguration({
3535
GrpcServerAutoConfiguration.class,
3636
GrpcServerFactoryAutoConfiguration.class,

0 commit comments

Comments
 (0)