Health checks for microservices

A health check is a special REST API implementation that you can use to validate the status of a microservice and its dependencies. With MicroProfile Health, microservices can self-check their health and publish their overall status to a defined endpoint.

A health check can assess anything that a microservice needs, including dependencies, system properties, database connections, endpoint connections, and resource availability. The overall status of the microservice depends on whether all the configured health checks pass. A microservice is considered available and reports an UP status if all the configured health checks are successful. If one or more health checks fail, the microservice is considered unavailable and reports a DOWN status. Services can report their availability to a defined endpoint by implementing the API that is provided by MicroProfile Health. A service orchestrator can use these status reports to decide how to manage and scale the microservices within an application. Health checks can also interact with Kubernetes liveness and readiness probes, and in MicroProfile Health 3.1 and later, Kubernetes startup probes.

For example, in a microservices-based banking application, you might implement health checks on the login microservice, the balance transfer microservice, and the bill pay microservice. If a health check on the balance transfer microservice detects a bug or deadlock, it returns a DOWN status. In this case, if the /health/live endpoint is configured in a Kubernetes liveness probe, the probe restarts the microservice automatically. Restarting the microservice saves the user from encountering downtime or an error and preserves the functions of the other microservices in the application.

In MicroProfile Health 3.1 and later, the startup check ensures that all the configured microservices have sufficient time to initialize before the liveness and readiness checks begin. This check is useful for applications that require extra startup time on their first initialization. It prevents the liveness and readiness checks from starting prematurely and potentially failing just because an application is not fully initialized. In the previous example, the startup check blocks the liveness and readiness checks from starting until it verifies that the login, balance transfer, and bill pay microservices are all fully initialized.

You can use MicroProfile Health with Open Liberty by enabling the MicroProfile Health feature in your server.xml file.

MicroProfile Health endpoints and annotations

MicroProfile Health provides the following four endpoints:

  • /health/ready
    This endpoint returns the readiness of a microservice, or whether it is ready to process requests. It corresponds to the Kubernetes readiness probe.

  • /health/live
    This endpoint returns the liveness of a microservice, or whether it encountered a bug or deadlock. If this check fails, the microservice is not running and can be stopped. This endpoint corresponds to the Kubernetes liveness probe, which automatically restarts the pod if the check fails.

  • /health/started
    In MicroProfile Health 3.1 and later, you can use this endpoint to determine whether your deployed applications are initialized, according to criteria that you define. It corresponds to the Kubernetes startup probe.

  • /health
    This endpoint aggregates the responses from the /health/live and /health/ready endpoints. It corresponds to the deprecated @Health annotation and is only available to provide compatibility with MicroProfile 1.0. If you are using MicroProfile 2.0 or higher, use the /health/ready or /health/live endpoints instead. The @Health annotation is not available in MicroProfile 4.0 or later or MicroProfile Health 3.0 or later.

To add readiness or liveness health checks to your microservice, create a class in your application code that implements the HealthCheck interface and add either the @Liveness or @Readiness annotation to it. These annotations specify the conditions that determine whether the health check passes or fails. In MicroProfile Health 3.1 and later, you can also add the @Startup annotation to create your own health check procedures that verify whether your microservices are fully initialized.

You can view health check results in a web browser by going to one of the endpoint URLs in the http://host:port_number/health/endpoint syntax. For example, if you configure the annotations in an application with a 9443 port number that runs on the same computer as your browser, the following endpoint URLs are available:

  • http://localhost:9443/health/live

  • http://localhost:9443/health/ready

  • http://localhost:9443/health/started (MicroProfile 3.1 and later)

For more information, see Adding health reports to microservices.

The @Liveness annotation

The following example demonstrates the @Liveness annotation in a class that implements the HealthCheck interface. In this example, the @Liveness annotation checks the heap memory usage. If heap memory usage is less than 90%, it returns an UP status. If memory usage exceeds 90%, it returns a DOWN status:

@Liveness
@ApplicationScoped
public class MemoryCheck implements HealthCheck {
 @Override
 public HealthCheckResponse call() {
        MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
        long memUsed = memoryBean.getHeapMemoryUsage().getUsed();
        long memMax = memoryBean.getHeapMemoryUsage().getMax();

        HealthCheckResponse response = HealthCheckResponse.named("heap-memory")
                .withData("used", memUsed)
                .withData("max", memMax)
                .status(memUsed < memMax * 0.9)
                .build();
        return response;
    }
}

For MicroProfile Health versions 2.x and earlier, the .status response is replaced by the .state response in the HealthCheckResponse class.

The following example shows the JSON response from the /health/live endpoint if heap memory usage is less than 90%. The first status indicates the overall status of all health checks that are returned from the endpoint. An UP status indicates that all the configured checks passed and that the service is available. A DOWN status indicates that one or more checks failed and the service is unavailable. The second status indicates the status of the particular check that is specified by the preceding name value, which in this example is heap-memory. In this example, the liveness check passed and because it is the only check that is configured, the overall status is UP:

{
  "status": "UP",
  "checks": [
    {
      "name": "heap-memory",
      "status": "UP",
      "data": {
        "used": "1475462",
        "max": ”51681681"
      }
    }
  ]
}

The @Readiness annotation

The next example shows the @Readiness annotation on a class that implements the HealthCheck interface. In this example, the @Readiness annotation checks for a database connection. If the connection is successful, it returns an UP status. If the connection is unsuccessful, it returns a DOWN status:

@Readiness
@ApplicationScoped
public class DatabaseReadyCheck implements HealthCheck {

    @Override
    public HealthCheckResponse call() {

        if (isDBConnected()) {
           return HealthCheckResponse.up(“databaseReady”);
        }
        else {
           return HealthCheckResponse.down(“databaseReady”);
        }
    }
}

The following example shows the JSON response from the /health/ready endpoint if the database connection is unsuccessful. The first status indicates the overall status of all health checks that are returned from the endpoint. The second status indicates the status of the particular check that is specified by the preceding name value, which in this example is databaseReady. In this example, the readiness check failed and returned a DOWN status so the overall status is also DOWN:

{
  "status": ”DOWN",
  "checks": [
    {
      "name": ”databaseReady",
      "status": ”DOWN",
    }
  ]
}

In MicroProfile Health versions 2.0 and later, the overall default readiness status is DOWN, with an empty response, until all the deployed applications in Open Liberty are started. In MicroProfile Health versions 3.0 and later, the mp.health.default.readiness.empty.response=UP MicroProfile Config property is available. You can specify this property to change the overall default readiness status to UP while deployed applications are starting up.

The @Startup annotation

In MicroProfile Health versions 3.1 and later, you can configure the @Startup` annotation to define health check procedures that determine whether your deployed application is fully initialized.

The following example shows the @Startup annotation on a class that implements the HealthCheck interface. In this example, the @Startup annotation checks for CPU usage as a measure of whether the application is fully initialized. If CPU usage is less than 90%, it returns an UP status. If CPU usage is greater than 90%, it returns a DOWN status:

@Startup
@ApplicationScoped
public class StartupCPUUsageCheck implements HealthCheck {

    @Override
    public HealthCheckResponse call() {

        if (getCpuUsage() < 0.90) {
           return HealthCheckResponse.up(“startupCpuUsage”);
        }
        else {
           return HealthCheckResponse.down(“startupCpuUsage”);
        }
    }
}

The following example shows the JSON response from the /health/started endpoint if the CPU usage is less than 90%. The first status indicates the overall status of all health checks that are returned from the endpoint. The second status indicates the status of the particular check that is specified by the preceding name value, which in this example is startupCpuUsage. In this example, the startup check passed and returned an UP status:

{
  "status": ”UP",
  "checks": [
    {
      "name": ”startupCpuUsage",
      "status": ”UP",
    }
  ]
}

In this example, the startup check determined that the application was initialized and the liveness and readiness checks can proceed. If the startup check returns a DOWN status, it blocks the liveness and readiness checks until the application is initialized and the startup check returns an UP status.

In MicroProfile Health versions 3.1 and later, the overall default startup status is DOWN, with an empty response, until all the deployed applications in Open Liberty are started. If you want to change the overall default startup status to UP while deployed applications are starting up, you can specify the mp.health.default.startup.empty.response=UP MicroProfile Config property.