Health checks are used to probe the state of a computing node from another machine (i.e. kubernetes service controller) with the primary target being cloud infrastructure environments where automated processes maintain the state of computing nodes.
In this scenario, health checks are used to determine if a computing node needs to be discarded (terminated, shutdown) and eventually replaced by another (healthy) instance.
It’s not intended (although could be used) as a monitoring solution for human operators.
The proposed solution breaks down into two parts:
-
A health check protocol and wireformat
-
A Java API to implement health check procedures
This project defines a protocol (wireformat, semantics and possible forms of interactions) between system components that need to determine the “liveliness” of computing nodes in a bigger architecture. A detailed description of the health check protocol can be found in the companion document.
The main API to provide health check procedures on the application level is the HealthCheck
interface:
@FunctionalInterface
public interface HealthCheck {
HealthCheckResponse call();
}
Applications are expected to provide health check procedures (implementation of a HealthCheck
), which will be used by the framework or runtime hosting the application to verify the healthiness of the computing node.
The runtime will call()
the HealthCheck
which in turn creates a HealthCheckResponse
that signals the health status to a consuming end:
public abstract class HealthCheckResponse {
public enum State { UP, DOWN }
public abstract String getName();
public abstract State getState();
public abstract Optional<Map<String, Object>> getData();
[...]
}
Application level code is expected to use one of static methods on HealthCheckResponse
to retrieve a HealthCheckResponseBuilder
used to construct a response, i.e. :
public class SuccessfulCheck implements HealthCheck {
@Override
public HealthCheckResponse call() {
return HealthCheckResponse.named("successful-check").up();
}
}
Within CDI contexts, beans that implement HealthCheck
and annotated with @Health
are discovered automatically and are invoked by the framework or runtime when the outermost protocol entry point (i.e. http://HOST:PORT/health
) receives an inbound request.
@Health
@ApplicationScoped
public class CheckDiskSpace implements HealthCheck {
public HealthCheckResponse call() {
[...]
}
}
It’s the responsibility of the runtime to gather all HealthCheckResponse
s for HealthCheck
s known to the runtime. This means an inbound HTTP request will lead to a series of invocations
on health check procedures and the runtime will provide a composite response, with a single overall outcome, i.e.:
``` { "outcome": "UP", "checks": [ { "name": "first-check", "state": "UP", "data": { "key": "foo", "foo": "bar" } }, { "name": "second-check", "state": "UP" } ] } ```
The companion document contains further information on forms of interaction and the wireformat.
Implementors of the API are expected to supply implementations of HealthCheckResponse
and HealthCheckResponseBuilder
by providing a HealthCheckResponseProvider
to their implementation. The HealthCheckResponseProvider
is discovered using the default JDK service loader.
A HealthCheckResponseProvider
is used internally to create a HealthCheckResponseBuilder
which is used to construct a HealthCheckResponse
. This pattern allows implementors to extend a HealthCheckResponse
and adapt it to their implementation needs. Common implementation details that fall into this category are invocation and security contexts or anything else required to map a HealthCheckResponse
to the outermost invocation protocol (i.e. HTTP/JSON).