spring-boot-actuator-Health原理

说明

在容器化部署下,如何判断服务是否健康通过 curl  127.0.0.1:8080/actuator/health检查 是否是健康状态

使用示例以sentinel为例

实现抽象类

public class SentinelHealthIndicator extends AbstractHealthIndicator {

    private DefaultListableBeanFactory beanFactory;

    private SentinelProperties sentinelProperties;

    public SentinelHealthIndicator(DefaultListableBeanFactory beanFactory,
            SentinelProperties sentinelProperties) {
        this.beanFactory = beanFactory;
        this.sentinelProperties = sentinelProperties;
    }

    @Override
    protected void doHealthCheck(Health.Builder builder) throws Exception {
        Map<String, Object> detailMap = new HashMap<>();

        // If sentinel isn't enabled, set the status up and set the enabled to false in
        // detail
        if (!sentinelProperties.isEnabled()) {
            detailMap.put("enabled", false);
            builder.up().withDetails(detailMap);
            return;
        }

        detailMap.put("enabled", true);

        // Check health of Dashboard
        boolean dashboardUp = true;
        List<Tuple2<String, Integer>> consoleServerList = TransportConfig
                .getConsoleServerList();
        if (CollectionUtils.isEmpty(consoleServerList)) {
            // If Dashboard isn't configured, it's OK and mark the status of Dashboard
            // with UNKNOWN.
            detailMap.put("dashboard",
                    new Status(Status.UNKNOWN.getCode(), "dashboard isn't configured"));
        }
        else {
            // If Dashboard is configured, send a heartbeat message to it and check the
            // result
            HeartbeatSender heartbeatSender = HeartbeatSenderProvider
                    .getHeartbeatSender();
            boolean result = heartbeatSender.sendHeartbeat();
            if (result) {
                detailMap.put("dashboard", Status.UP);
            }
            else {
                // If failed to send heartbeat message, means that the Dashboard is DOWN
                dashboardUp = false;
                detailMap.put("dashboard",
                        new Status(Status.UNKNOWN.getCode(), String.format(
                                "the dashboard servers [%s] one of them can't be connected",
                                consoleServerList)));
            }
        }

        // Check health of DataSource
        boolean dataSourceUp = true;
        Map<String, Object> dataSourceDetailMap = new HashMap<>();
        detailMap.put("dataSource", dataSourceDetailMap);

        // Get all DataSources and each call loadConfig to check if it's OK
        // If no Exception thrown, it's OK
        // Note:
        // Even if the dynamic config center is down, the loadConfig() might return
        // successfully
        // e.g. for Nacos client, it might retrieve from the local cache)
        // But in most circumstances it's okay
        Map<String, AbstractDataSource> dataSourceMap = beanFactory
                .getBeansOfType(AbstractDataSource.class);
        for (Map.Entry<String, AbstractDataSource> dataSourceMapEntry : dataSourceMap
                .entrySet()) {
            String dataSourceBeanName = dataSourceMapEntry.getKey();
            AbstractDataSource dataSource = dataSourceMapEntry.getValue();
            try {
                dataSource.loadConfig();
                dataSourceDetailMap.put(dataSourceBeanName, Status.UP);
            }
            catch (Exception e) {
                // If one DataSource failed to loadConfig, means that the DataSource is
                // DOWN
                dataSourceUp = false;
                dataSourceDetailMap.put(dataSourceBeanName,
                        new Status(Status.UNKNOWN.getCode(), e.getMessage()));
            }
        }

        // If Dashboard and DataSource are both OK, the health status is UP
        if (dashboardUp && dataSourceUp) {
            builder.up().withDetails(detailMap);
        }
        else {
            builder.unknown().withDetails(detailMap);
        }
    }

}
View Code

注入容器

        @Bean
    @ConditionalOnMissingBean
    @ConditionalOnEnabledHealthIndicator("sentinel") //management.health.sentinel.enabled=false 可禁用
    public SentinelHealthIndicator sentinelHealthIndicator(
            DefaultListableBeanFactory beanFactory,
            SentinelProperties sentinelProperties) {
        return new SentinelHealthIndicator(beanFactory, sentinelProperties);
    }

访问CURL端点

image

 初始化原理

1、初始化HealthIndicatorRegistry

org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorAutoConfiguration#healthIndicatorRegistry

 @Bean
    @ConditionalOnMissingBean({HealthIndicatorRegistry.class})
    public HealthIndicatorRegistry healthIndicatorRegistry(ApplicationContext applicationContext) {
    //内部会从容器获取HealthIndicator实现类 里面就有我们的sentinel实现类
        return HealthIndicatorRegistryBeans.get(applicationContext);
    }

 

2、org.springframework.boot.actuate.autoconfigure.health.HealthIndicatorRegistryBeans#get

    public static HealthIndicatorRegistry get(ApplicationContext applicationContext) {
        Map<String, HealthIndicator> indicators = new LinkedHashMap();
        //从容器获取
        indicators.putAll(applicationContext.getBeansOfType(HealthIndicator.class));
        if (ClassUtils.isPresent("reactor.core.publisher.Flux", (ClassLoader)null)) {
            (new ReactiveHealthIndicators()).get(applicationContext).forEach(indicators::putIfAbsent);
        }

        HealthIndicatorRegistryFactory factory = new HealthIndicatorRegistryFactory();
        //构建 HealthIndicatorRegistry
        return factory.createHealthIndicatorRegistry(indicators);
    }

3、返回含有所有health健康检查的 registry

     public HealthIndicatorRegistry createHealthIndicatorRegistry(
            Map<String, HealthIndicator> healthIndicators) {
        Assert.notNull(healthIndicators, "HealthIndicators must not be null");
        //初始化
        return initialize(new DefaultHealthIndicatorRegistry(), healthIndicators);
    }

    protected <T extends HealthIndicatorRegistry> T initialize(T registry,
            Map<String, HealthIndicator> healthIndicators) {
        for (Map.Entry<String, HealthIndicator> entry : healthIndicators.entrySet()) {
        //这里主要是获取bean的名字 截取掉 如 sentinelHealthIndicator  截取掉HealthIndicator  name为sentinel
            String name = this.healthIndicatorNameFactory.apply(entry.getKey());
            registry.register(name, entry.getValue());
        }
        return registry;
    }

执行原理

1、actor模式会在Spring MVC注册一个mappring入口为

org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping.OperationHandler#handle

->

org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping.ServletWebOperationAdapter#handle

->

org.springframework.boot.actuate.endpoint.annotation.AbstractDiscoveredOperation#invoke

->

org.springframework.boot.actuate.health.HealthEndpointWebExtension#health

->

org.springframework.boot.actuate.health.HealthEndpointWebExtension#health

     @ReadOperation
    public WebEndpointResponse<Health> health(SecurityContext securityContext) {
        //内部就是从 初始化的registry遍历所有的处理器获取检查结果
        return this.responseMapper.map(this.delegate.health(), securityContext);
    }

2、org.springframework.boot.actuate.health.HealthEndpoint#health

image

 3、org.springframework.boot.actuate.health.CompositeHealthIndicator#health

    @Override
    public Health health() {
        Map<String, Health> healths = new LinkedHashMap<>();
        for (Map.Entry<String, HealthIndicator> entry : this.registry.getAll()
                .entrySet()) {
                //遍历执行 获取结果
            healths.put(entry.getKey(), entry.getValue().health());
        }
        //进行聚合返回
        return this.aggregator.aggregate(healths);
    }

 

posted @ 2025-11-04 16:05  意犹未尽  阅读(13)  评论(0)    收藏  举报