- Helidon SE Health Check Guide
This guide describes how to create a sample Helidon SE project that can be used to run some basic examples using both built-in and custom health-checks.
What You Need
For this 15 minute tutorial, you will need the following:
| A Helidon {upper-case-flavor} Application | You can use your own application or use the Helidon {upper-case-flavor} Quickstart to create a sample application. |
| Java SE 11 (Open JDK 11) | Helidon requires Java 11+. |
| Maven 3.6.1+ | Helidon requires Maven 3.6.1+. |
| Docker 18.09+ | You need Docker if you want to build and deploy Docker containers. |
| Kubectl 1.16.5+ | If you want to deploy to Kubernetes, you need kubectl and a Kubernetes cluster (you can install one on your desktop). |
java -version
mvn --version
docker --version
kubectl version --short# On Mac
export JAVA_HOME=`/usr/libexec/java_home -v 11`
# On Linux
# Use the appropriate path to your JDK
export JAVA_HOME=/usr/lib/jvm/jdk-11Create a Sample SE Project
Generate the project sources using the Helidon SE Maven archetype. The result is a simple project that can be used for the examples in this guide.
mvn -U archetype:generate -DinteractiveMode=false \
-DarchetypeGroupId=io.helidon.archetypes \
-DarchetypeArtifactId=helidon-quickstart-se \
-DarchetypeVersion=2.6.14 \
-DgroupId=io.helidon.examples \
-DartifactId=helidon-quickstart-se \
-Dpackage=io.helidon.examples.quickstart.seUsing the Built-In Health Checks
Helidon has a set of built-in health checks that can be optionally enabled to report various health-check statuses that are commonly used:
deadlock detection
available disk space
available heap memory
The following example will demonstrate how to use the built-in health-checks. These examples are all executed from the root directory of your project (helidon-quickstart-se).
<dependency>
<groupId>io.helidon.health</groupId>
<artifactId>helidon-health-checks</artifactId>
</dependency>Main.java, and the createRouting method:private static Routing createRouting(Config config) {
HealthSupport health = HealthSupport.builder()
.addLiveness(HealthChecks.healthChecks())
.build();
return Routing.builder()
.register(health)
.build();
}- Add built-in health-checks (requires the
helidon-health-checksdependency). - Register the created health support with web server routing (adds the
/healthendpoint).
mvn package -DskipTests=true
java -jar target/helidon-quickstart-se.jarcurl http://localhost:8080/health{
"outcome": "UP",
"status": "UP",
"checks": [
{
"name": "deadlock",
"state": "UP",
"status": "UP"
},
{
"name": "diskSpace",
"state": "UP",
"status": "UP",
"data": {
"free": "319.58 GB",
"freeBytes": 343144304640,
"percentFree": "68.63%",
"total": "465.63 GB",
"totalBytes": 499963174912
}
},
{
"name": "heapMemory",
"state": "UP",
"status": "UP",
"data": {
"free": "196.84 MB",
"freeBytes": 206404016,
"max": "3.56 GB",
"maxBytes": 3817865216,
"percentFree": "98.66%",
"total": "245.50 MB",
"totalBytes": 257425408
}
}
]
}In MicroProfile Health 2.0 outcome and state were replaced by status in the JSON response wire format. Helidon currently provides both fields for backwards compatibility, but use of outcome and state is deprecated and will be removed in a future release. You should rely on status instead.
Custom Liveness Health Checks
You can create application specific custom health checks and integrate them with Helidon using the HealthSupport class, which is a WebServer service that contains a collection of registered HealthCheck instances. When queried, it invokes the registered health check and returns a response with a status code representing the overall state of the application.
<dependency>
<groupId>io.helidon.health</groupId>
<artifactId>helidon-health</artifactId>
</dependency>HealthSupport builder in the Main.createRouting method:HealthSupport health = HealthSupport.builder()
.addLiveness(() -> HealthCheckResponse.named("LivenessCheck")
.up()
.withData("time", System.currentTimeMillis())
.build())
.build();- Add a custom liveness health check. This example returns
UPand current time.
curl http://localhost:8080/health{
"outcome": "UP",
"checks": [
{
"name": "LivenessCheck",
"state": "UP",
"data": {
"time": 1546958376613
}
}
]
}Custom Readiness Health Check
You can add a readiness check to indicate that the application is ready to be used. In this example, the server will wait five seconds before it becomes ready.
readyTime variable to the Main class, then set it five seconds after the application starts:import java.util.concurrent.atomic.AtomicLong;
public final class Main {
private static AtomicLong readyTime = new AtomicLong(0);
...
static WebServer startServer() throws IOException {
...
server.start() ...
// Server threads are not daemon. No need to block. Just react.
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
readyTime.set(System.currentTimeMillis());
return server;- Import AtomicLong.
- Declare the
readyTimevariable. - Sleep five seconds.
- Set the
readyTimeto the time when the server became ready.
HealhSupport builder in the Main.createRouting method:HealthSupport health = HealthSupport.builder()
.addLiveness(() -> HealthCheckResponse.named("LivenessCheck")
.up()
.withData("time", System.currentTimeMillis())
.build())
.addReadiness(() -> HealthCheckResponse.named("ReadinessCheck")
.state (readyTime.get() != 0 )
.withData( "time", readyTime.get())
.build())
.build();- Add the readiness check.
curl -v http://localhost:8080/health/ready...
< HTTP/1.1 503 Service Unavailable
...
{
"outcome": "DOWN",
"status": "DOWN",
"checks": [
{
"name": "ReadinessCheck",
"state": "DOWN",
"status": "DOWN",
"data": {
"time,": 0
}
}
]
}- The HTTP status is
503since the application is not ready.
curl -v http://localhost:8080/health/ready...
< HTTP/1.1 200 OK
...
{
"outcome": "UP",
"status": "UP",
"checks": [
{
"name": "ReadinessCheck",
"state": "UP",
"status": "UP",
"data": {
"time,": 1566243562097
}
}
]
}- The HTTP status is
200indicating that the application is ready.
When using the health-check URLs, you can get the following health-check data
liveness only - http://localhost:8080/health/live
readiness only - http://localhost:8080/health/ready
both - http://localhost:8080/health
curl http://localhost:8080/health{
"outcome": "UP",
"status": "UP",
"checks": [
{
"name": "LivenessCheck",
"state": "UP",
"status": "UP",
"data": {
"time": 1566244094548
}
},
{
"name": "ReadinessCheck",
"state": "UP",
"status": "UP",
"data": {
"time,": 1566244093012
}
}
]
}Combine Built-In and Custom Health Checks
You can combine built-in and custom health-checks using the same HealthSupport builder.
Main.createRouting method:HealthSupport health = HealthSupport.builder()
.addLiveness(HealthChecks.healthChecks())
.addLiveness(() -> HealthCheckResponse.named("LivenessCheck")
.up()
.withData("time", System.currentTimeMillis())
.build())
.addReadiness(() -> HealthCheckResponse.named("ReadinessCheck")
.state (readyTime.get() != 0 )
.withData( "time", readyTime.get())
.build())
.build();- Add the built-in health-checks back to
HealthSupportbuilder.
curl http://localhost:8080/health{
"outcome": "UP",
"status": "UP",
"checks": [
{
"name": "LivenessCheck",
"state": "UP",
"status": "UP",
"data": {
"time": 1566245527673
}
},
{
"name": "ReadinessCheck",
"state": "UP",
"status": "UP",
"data": {
"time,": 1566245527620
}
},
{
"name": "deadlock",
"state": "UP",
"status": "UP"
},
{
"name": "diskSpace",
"state": "UP",
"status": "UP",
"data": {
"free": "326.17 GB",
"freeBytes": 350224424960,
"percentFree": "70.05%",
"total": "465.63 GB",
"totalBytes": 499963174912
}
},
{
"name": "heapMemory",
"state": "UP",
"status": "UP",
"data": {
"free": "247.76 MB",
"freeBytes": 259791680,
"max": "4.00 GB",
"maxBytes": 4294967296,
"percentFree": "99.80%",
"total": "256.00 MB",
"totalBytes": 268435456
}
}
]
}Custom Health Check URL Path
You can use a custom URL path for heath checks by setting the WebContext. In the following example, only the liveness URL is changed, but you can do the same for the readiness and default health-checks.
Main.createRouting method:HealthSupport health = HealthSupport.builder()
.webContext("/probe/live")
.addLiveness(() -> HealthCheckResponse.named("livenessProbe")
.up()
.withData("time", System.currentTimeMillis())
.build())
.build();- Change the liveness URL path using a
WebContext.
/probe/live:curl http://localhost:8080/probe/live{
"outcome": "UP",
"checks": [
{
"name": "livenessProbe",
"state": "UP",
"data": {
"time": 1546958376613
}
}
]
}Using Liveness and Readiness Health Checks with Kubernetes
The following example shows how to integrate the Helidon health API in an application that implements health endpoints for the Kubernetes liveness and readiness probes.
HealthSupport builder in the Main.createRouting method to use the built-in liveness checks, a custom liveness check, and a readiness check:HealthSupport health = HealthSupport.builder()
.addLiveness(HealthChecks.healthChecks())
.addLiveness(() -> HealthCheckResponse.named("LivenessCheck")
.up()
.withData("time", System.currentTimeMillis())
.build())
.addReadiness(() -> HealthCheckResponse.named("ReadinessCheck")
.state (readyTime.get() != 0 )
.withData( "time", readyTime.get())
.build())
.build();- Add built-in health-checks.
- Add a custom liveness check.
- Add a custom readiness check.
curl http://localhost:8080/health/live
curl http://localhost:8080/health/readydocker build -t helidon-quickstart-se .health.yaml, with the following content:kind: Service
apiVersion: v1
metadata:
name: helidon-health
labels:
app: helidon-health
spec:
type: NodePort
selector:
app: helidon-health
ports:
- port: 8080
targetPort: 8080
name: http
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: helidon-health
spec:
replicas: 1
selector:
matchLabels:
app: helidon-health
template:
metadata:
labels:
app: helidon-health
version: v1
spec:
containers:
- name: helidon-health
image: helidon-quickstart-se
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 2
timeoutSeconds: 3
---- A service of type
NodePortthat serves the default routes on port8080. - A deployment with one replica of a pod.
- The HTTP endpoint for the liveness probe.
- The liveness probe configuration.
- The HTTP endpoint for the readiness probe.
- The readiness probe configuration.
kubectl apply -f ./health.yamlkubectl get service/helidon-healthNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
helidon-health NodePort 10.107.226.62 <none> 8080:30116/TCP 4s - A service of type
NodePortthat serves the default routes on port30116.
curl http://localhost:30116/healthkubectl delete -f ./health.yamlSummary
This guide demonstrated how to use health checks in a Helidon SE application as follows:
Access the default health-check
Create and use custom readiness and liveness checks
Customize the health-check root path
Integrate Helidon health-check with Kubernetes
Refer to the following reference for additional information:
Helidon Javadoc at https://helidon.io/docs/latest/apidocs/index.html?overview-summary.html