我有几个自定义检查,用于我的/致动器/运行状况检查。其中一些调用其他服务,以查看它们是否可访问。不幸的是,这可能需要一些时间。因此,我希望spring boot能够并行地进行这些检查。
演示:
@Component
public class HealthCheck1 extends AbstractHealthIndicator {
@Override
protected void doHealthCheck(Health.Builder builder) throws InterruptedException {
Thread.sleep(5000); // actually make a service call
builder.up();
}
}
@Component
public class HealthCheck2 extends AbstractHealthIndicator {
@Override
protected void doHealthCheck(Health.Builder builder) throws InterruptedException {
Thread.sleep(5000); // actually make a service call
builder.up();
}
}$ time http localhost:8080/actuator/health
HTTP/1.1 200
Connection: keep-alive
Content-Type: application/vnd.spring-boot.actuator.v3+json
Date: Fri, 03 Jan 2020 14:54:31 GMT
Keep-Alive: timeout=60
Transfer-Encoding: chunked
{
"components": {
"diskSpace": {
"details": {
"free": 398020632576,
"threshold": 10485760,
"total": 499963174912
},
"status": "UP"
},
"healthCheck1": {
"status": "UP"
},
"healthCheck2": {
"status": "UP"
},
"ping": {
"status": "UP"
}
},
"status": "UP"
}
real 0m10.359s
user 0m0.242s
sys 0m0.036s这表明检查当前是按顺序进行的。有没有办法改变这一点?
发布于 2020-01-08 14:30:04
由于目前不可能像@Roland Weisleder在评论中指出的那样并行运行所有健康检查,您可以执行以下解决方法:
而不是在HealthCheck1中进行实际的检查
@Component
public class MyCustomBeanCheck {
private boolean isOk;
// run periodically
public void doCheck() {
isOk = contactServiceTakesALongTime();
}
public boolean isOk() {
return isOk;
}
}
public class HealthCheck1 extends AbstractHealthIndicator {
private MyCustomBeanCheck myCustomBeanCheck;
protected void doHealthCheck(Health.Builder builder) throws InterruptedException {
if(myCustomBeanCheck.isOk()) { // immediate access
builder.up();
} else {
builder.down();
}
}
}发布于 2020-01-08 22:26:37
我找到了另一种解决方法:
@RestController
public class SystemCheckBoundary {
private static final int TIMEOUT_IN_SECONDS = 10;
@AllArgsConstructor
private static class CheckResult {
HealthIndicator check;
CompletableFuture<MyHealth> futureHealth;
public String getCheckName() {
String name = check.getClass().getSimpleName();
if (name.endsWith("HealthIndicator")) {
name = name.substring(0, name.length() - "HealthIndicator".length());
}
return Character.toLowerCase(name.charAt(0)) + name.substring(1);
}
public MyHealth getHealth() {
try {
return futureHealth.get(TIMEOUT_IN_SECONDS, SECONDS);
} catch (InterruptedException | ExecutionException e) {
return new StaticHealth(DOWN);
} catch (TimeoutException e) {
return new StaticHealth(DOWN);
}
}
}
@JsonInclude(NON_EMPTY)
private interface MyHealth {
@JsonUnwrapped
Status getStatus();
Map<String, Object> getDetails();
}
@AllArgsConstructor
private static class DelegatingHealth implements MyHealth {
private Health health;
@Override
public Status getStatus() {
return health.getStatus();
}
@Override
public Map<String, Object> getDetails() {
return health.getDetails();
}
}
@Getter
@RequiredArgsConstructor
private static class StaticHealth implements MyHealth {
@NonNull
private Status status;
private Map<String, Object> details;
}
@Value
private static class SystemHealth {
Map<String, MyHealth> components;
public String getStatus() {
return components.values().stream()
.map(MyHealth::getStatus)
.reduce(UP, this::min)
.getCode();
}
private Status min(Status a, Status b) {
List<Status> statuses = asList(DOWN, UP, UNKNOWN);
return statuses.indexOf(a) < statuses.indexOf(b) ? a : b;
}
}
@Autowired
private List<HealthIndicator> checks;
@Resource
private TaskExecutor executor;
@GetMapping("/myactuator/health")
public SystemHealth check() {
return new SystemHealth(checks.stream()
.map(this::getHealth)
.collect(toList()).stream() // terminate stream so checks are running in parallel; don't call get() to early
.collect(toMap(CheckResult::getCheckName, CheckResult::getHealth)));
}
private CheckResult getHealth(HealthIndicator healthIndicator) {
return new CheckResult(healthIndicator, supplyAsync(() ->
new DelegatingHealth(healthIndicator.getHealth(true)), executor));
}
}请注意,这不会是执行器的一部分,而是一个“普通”的RestController。
发布于 2021-12-18 16:34:46
如果您使用的是Spring Boot >= 2.2,您可以使用单独的库spring-boot-async-health-indicator,通过简单地用@AsyncHealth注释它们来使您的健康检查并行(和异步)运行。
示例:
@AsyncHealth
@Component
public class HealthCheck1 implements HealthIndicator {
@Override
public Health health() {
Thread.sleep(5000); // makes a service call on a different thread than /health
return Health.up().build();
}
}免责声明:我创建这个库正是为了这个目的
https://stackoverflow.com/questions/59580882
复制相似问题