首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >并行执行Spring Boot /actuator/heath run检查

并行执行Spring Boot /actuator/heath run检查
EN

Stack Overflow用户
提问于 2020-01-03 23:06:09
回答 3查看 780关注 0票数 3

我有几个自定义检查,用于我的/致动器/运行状况检查。其中一些调用其他服务,以查看它们是否可访问。不幸的是,这可能需要一些时间。因此,我希望spring boot能够并行地进行这些检查。

演示:

代码语言:javascript
复制
@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();
    }
}
代码语言:javascript
复制
$ 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

这表明检查当前是按顺序进行的。有没有办法改变这一点?

EN

回答 3

Stack Overflow用户

发布于 2020-01-08 14:30:04

由于目前不可能像@Roland Weisleder在评论中指出的那样并行运行所有健康检查,您可以执行以下解决方法:

而不是在HealthCheck1中进行实际的检查

  1. 创建另一个bean,让long check
  2. 周期性地在某个线程中运行它(使用@ Scheduled,Scheduled Executor或其他)
  3. 让它在一个字段中本地存储决策,这里没有多线程访问,该bean是一个单例,所以你应该可以运行了。
  4. 将该bean注入HealthCheck并获取状态。

代码语言:javascript
复制
@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();
       }
    }
}
票数 2
EN

Stack Overflow用户

发布于 2020-01-08 22:26:37

我找到了另一种解决方法:

代码语言:javascript
复制
@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。

票数 0
EN

Stack Overflow用户

发布于 2021-12-18 16:34:46

如果您使用的是Spring Boot >= 2.2,您可以使用单独的库spring-boot-async-health-indicator,通过简单地用@AsyncHealth注释它们来使您的健康检查并行(和异步)运行。

示例:

代码语言:javascript
复制
@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();
    }

}

免责声明:我创建这个库正是为了这个目的

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59580882

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档