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.

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.

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 three 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
    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. You can view the results of the health checks by pointing your browser to one of the endpoint URLs, for example http://localhost:9443/health/live or http://localhost:9443/health/ready, where 9443 is the port number for your application. For more information, see Adding health reports to microservices.

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 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.