- Helidon MP Health Check Guide
This guide describes how to create a sample MicroProfile (MP) project that can be used to run some basic examples using both built-in and custom health checks with Helidon MP.
What You Need
For this 15 minute tutorial, you will need the following:
| A Helidon MP Application | You can use your own application or use the Helidon MP Quickstart to create a sample application. |
| Java SE 17 (Open JDK 17) | Helidon requires Java 17+. |
| 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# On Mac
export JAVA_HOME=`/usr/libexec/java_home -v 17`
# On Linux
# Use the appropriate path to your JDK
export JAVA_HOME=/usr/lib/jvm/jdk-17Create a Sample MP Project
Generate the project sources using the Helidon MP 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-mp \
-DarchetypeVersion=3.2.16 \
-DgroupId=io.helidon.examples \
-DartifactId=helidon-quickstart-mp \
-Dpackage=io.helidon.examples.quickstart.mpUsing the Built-In Health Checks
Helidon has a set of built-in health checks that are automatically 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-mp).
pom.xml file to include the built-in health checks:<dependency>
<groupId>io.helidon.health</groupId>
<artifactId>helidon-health-checks</artifactId>
<scope>runtime</scope>
</dependency>mvn package -DskipTests=true
java -jar target/helidon-quickstart-mp.jarcurl http://localhost:8080/health{
"status": "UP",
"checks": [
{
"name": "deadlock",
"status": "UP"
},
{
"name": "diskSpace",
"status": "UP",
"data": {
"free": "325.54 GB",
"freeBytes": 349543358464,
"percentFree": "69.91%",
"total": "465.63 GB",
"totalBytes": 499963174912
}
},
{
"name": "heapMemory",
"status": "UP",
"data": {
"free": "230.87 MB",
"freeBytes": 242085696,
"max": "3.56 GB",
"maxBytes": 3817865216,
"percentFree": "98.90%",
"total": "271.00 MB",
"totalBytes": 284164096
}
}
]
}Custom Liveness Health Checks
You can create application-specific custom health checks and integrate them with Helidon using CDI. The following example shows how to add a custom liveness health check.
GreetLivenessCheck class with the following content:package io.helidon.examples.quickstart.mp;
import jakarta.enterprise.context.ApplicationScoped;
import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.Liveness;
@Liveness
@ApplicationScoped
public class GreetLivenessCheck implements HealthCheck {
private GreetingProvider provider;
@Override
public HealthCheckResponse call() {
return HealthCheckResponse.named("LivenessCheck")
.up()
.withData("time", System.currentTimeMillis())
.build();
}
}- Annotation indicating this is a liveness health check.
- Annotation indicating there is a single liveness
HealthCheckobject during the lifetime of the application. - Build the HealthCheckResponse with status
UPand the current time.
curl http://localhost:8080/health/live{
"status": "UP",
"checks": [
{
"name": "LivenessCheck",
"status": "UP",
"data": {
"time": 1566338255331
}
}
]
}Custom Readiness Health Checks
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.
GreetReadinessCheck class with the following content:package io.helidon.examples.quickstart.mp;
import java.time.Duration;
import java.util.concurrent.atomic.AtomicLong;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.context.Initialized;
import jakarta.enterprise.event.Observes;
import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.Readiness;
@Readiness
@ApplicationScoped
public class GreetReadinessCheck implements HealthCheck {
private final AtomicLong readyTime = new AtomicLong(0);
@Override
public HealthCheckResponse call() {
return HealthCheckResponse.named("ReadinessCheck")
.status(isReady())
.withData("time", readyTime.get())
.build();
}
public void onStartUp(
@Observes @Initialized(ApplicationScoped.class) Object init) {
readyTime.set(System.currentTimeMillis());
}
/**
* Become ready after 5 seconds
*
* @return true if application ready
*/
private boolean isReady() {
return Duration.ofMillis(System.currentTimeMillis() - readyTime.get()).getSeconds() >= 5;
}
}- Include additional imports.
- Annotation indicating that this is a readiness health check.
- Build the
HealthCheckResponsewith statusUPafter five seconds, elseDOWN. - Record the time at startup.
curl -v http://localhost:8080/health/ready< HTTP/1.1 503 Service Unavailable - The HTTP status is
503since the application is not ready.
{
"status": "DOWN",
"checks": [
{
"name": "ReadinessCheck",
"status": "DOWN",
"data": {
"time": 1566399775700
}
}
]
}curl -v http://localhost:8080/health/ready< HTTP/1.1 200 OK - The HTTP status is
200indicating that the application is ready.
{
"status": "UP",
"checks": [
{
"name": "ReadinessCheck",
"status": "UP",
"data": {
"time": 1566399775700
}
}
]
}Custom Startup Health Checks
You can add a startup check to indicate whether or not the application has initialized to the point that the other health checks make sense. In this example, the server will wait eight seconds before it declares itself started.
GreetStartedCheck class with the following content:package io.helidon.examples.quickstart.mp;
import java.time.Duration;
import java.util.concurrent.atomic.AtomicLong;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.context.Initialized;
import jakarta.enterprise.event.Observes;
import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.Started;
@Started
@ApplicationScoped
public class GreetStartedCheck implements HealthCheck {
private final AtomicLong readyTime = new AtomicLong(0);
@Override
public HealthCheckResponse call() {
return HealthCheckResponse.named("StartedCheck")
.status(isStarted())
.withData("time", readyTime.get())
.build();
}
public void onStartUp(
@Observes @Initialized(ApplicationScoped.class) Object init) {
readyTime.set(System.currentTimeMillis());
}
/**
* Become ready after 5 seconds
*
* @return true if application ready
*/
private boolean isStarted() {
return Duration.ofMillis(System.currentTimeMillis() - readyTime.get()).getSeconds() >= 8;
}
}- Include additional imports.
- Annotation indicating that this is a startup health check.
- Build the
HealthCheckResponsewith statusUPafter eight seconds, elseDOWN. - Record the time at startup of Helidon; the application will declare itself as started eight seconds later.
curl -v http://localhost:8080/health/started< HTTP/1.1 503 Service Unavailable - The HTTP status is
503since the application has not started.
{
"status": "DOWN",
"checks": [
{
"name": "StartedCheck",
"status": "DOWN",
"data": {
"time": 1566399775700
}
}
]
}curl -v http://localhost:8080/health/started< HTTP/1.1 200 OK - The HTTP status is
200indicating that the application is started.
{
"status": "UP",
"checks": [
{
"name": "StartedCheck",
"status": "UP",
"data": {
"time": 1566399775700
}
}
]
}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
startup checks only - http://localhost:8080/health/started
all health check data - http://localhost:8080/health
curl http://localhost:8080/health{
"status": "UP",
"checks": [
{
"name": "LivenessCheck",
"status": "UP",
"data": {
"time": 1566403431536
}
},
{
"name": "ReadinessCheck",
"status": "UP",
"data": {
"time": 1566403280639
}
},
{
"name": "StartedCheck",
"status": "UP",
"data": {
"time": 1566403280639
}
},
{
"name": "deadlock",
"state": "UP",
"status": "UP"
},
{
"name": "diskSpace",
"state": "UP",
"status": "UP",
"data": {
"free": "325.50 GB",
"freeBytes": 349500698624,
"percentFree": "69.91%",
"total": "465.63 GB",
"totalBytes": 499963174912
}
},
{
"name": "heapMemory",
"state": "UP",
"status": "UP",
"data": {
"free": "231.01 MB",
"freeBytes": 242235928,
"max": "3.56 GB",
"maxBytes": 3817865216,
"percentFree": "98.79%",
"total": "275.00 MB",
"totalBytes": 288358400
}
}
]
}Custom Health Root Path and Port
You can specify a custom port and root context for the root health endpoint path. However, you cannot use different ports, such as http://localhost:8080/myhealth and http://localhost:8081/myhealth/live. Likewise, you cannot use different paths, such as http://localhost:8080/health and http://localhost:8080/probe/live.
The example below will change the root path.
application.yaml in the resources directory with the following contents:health:
web-context: "myhealth" - The web-context specifies a new root path for the health endpoint.
/myhealth root:curl http://localhost:8080/myhealth
curl http://localhost:8080/myhealth/live
curl http://localhost:8080/myhealth/ready
curl http://localhost:8080/myhealth/startedThe following example will change the root path and the health port.
server:
port: 8080
host: "localhost"
sockets:
health:
port: 8081
bind-address: "localhost"
health:
routing: "health"
web-context: "myhealth"- The default port for the application.
- The name of the new socket, it can be any name, this example uses
health. - The port for the new health socket.
- The health endpoint routing uses the new socket
health.
8081 and /myhealth:curl http://localhost:8081/myhealth
curl http://localhost:8081/myhealth/live
curl http://localhost:8081/myhealth/ready
curl http://localhost:8081/myhealth/startedUsing Liveness, Readiness, and Startup Health Checks with Kubernetes
The following example shows how to integrate the Helidon health check API with an application that implements health endpoints for the Kubernetes liveness, readiness, and startup probes.
Delete the contents of application.yaml so that the default health endpoint path and port are used.
curl http://localhost:8080/healthdocker build -t helidon-quickstart-mp .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-mp
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
startupProbe:
httpGet:
path: /health/started
port: 8080
initialDelaySeconds: 8
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 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.
- The HTTP endpoint for the startup probe.
- The startup 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 MP application as follows:
Access the default health checks
Create and use custom readiness, liveness, and startup checks
Customize the health check root path and port
Integrate Helidon health check API with Kubernetes
Refer to the following references for additional information: