To expand KrzysztofS's answer:
First, create a custom health indicator like this:
import java.util.concurrent.atomic.AtomicBoolean;
import org.springframework.boot.actuate.availability.ReadinessStateHealthIndicator;
import org.springframework.boot.availability.ApplicationAvailability;
import org.springframework.boot.availability.AvailabilityState;
import org.springframework.boot.availability.ReadinessState;
import org.springframework.stereotype.Component;
@Component
public class MyCustomReadinessIndicator extends ReadinessStateHealthIndicator {
private final AtomicBoolean ready = new AtomicBoolean();
public MyCustomReadinessIndicator(ApplicationAvailability availability) {
super(availability);
}
@Override
protected AvailabilityState getState(ApplicationAvailability applicationAvailability) {
return ready.get()
? ReadinessState.ACCEPTING_TRAFFIC
: ReadinessState.REFUSING_TRAFFIC;
}
public void markAsReady() {
if (ready.get()) {
throw new IllegalStateException("Already initialized");
}
ready.set(true);
}
}
This must be a bean, or Spring won't be able to discover it.
@Autowire
your component to your service or another component, and call its markAsReady()
function when this indicator should switch into "ready" state.
Next, add the name of the bean1 into "include" block for "readiness" group in your application.yaml file (if you're using application.properties, figure it out yourself).
management:
endpoint:
health:
group:
readiness:
include: readinessState, myCustomReadinessIndicator
show-components: always
show-details: always
probes:
enabled: true
Next, try running your application and opening various Actuator endpoints.
Endpoint |
ready state of your indicator |
HTTP response code |
JSON response |
/actuator/health |
false |
503 |
{
"status": "OUT_OF_SERVICE",
"components":
{
"myCustomReadinessIndicator":
{
"status": "OUT_OF_SERVICE"
},
"livenessState":
{
"status": "UP"
},
"readinessState":
{
"status": "UP"
}
},
"groups": ["liveness", "readiness"]
} |
/actuator/health/liveness |
false |
200 |
{"status":"UP"} |
/actuator/health/readiness |
false |
503 |
{
"status": "OUT_OF_SERVICE",
"components":
{
"myCustomReadinessIndicator":
{
"status": "OUT_OF_SERVICE"
},
"readinessState":
{
"status": "UP"
}
}
} |
/actuator/health |
true |
200 |
{
"status": "UP",
"components":
{
"myCustomReadinessIndicator":
{
"status": "UP"
},
"livenessState":
{
"status": "UP"
},
"readinessState":
{
"status": "UP"
}
},
"groups": ["liveness", "readiness"]
} |
/actuator/health/liveness |
true |
200 |
{"status":"UP"} |
/actuator/health/readiness |
true |
200 |
{
"status": "UP",
"components":
{
"myCustomReadinessIndicator":
{
"status": "UP"
},
"readinessState":
{
"status": "UP"
}
}
} |
Now, you need to set /actuator/health/readiness
in your readiness probe's path
readinessProbe:
initialDelaySeconds: 10
periodSeconds: 10
httpGet:
path: /actuator/health/readiness
port: 12345
Related: set liveness probe's path to /actuator/health/liveness
not to /actuator/health
, since /actuator/health
will return 503 if your indicator isn't ready yet, even though livenessState
is "UP".
1 Bean name is usually camel-cased name of the class, starting with lowercase letter, but you can override it by providing name in component annotation: @Component("overriddenName")