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); } } }
注入容器
@Bean @ConditionalOnMissingBean @ConditionalOnEnabledHealthIndicator("sentinel") //management.health.sentinel.enabled=false 可禁用 public SentinelHealthIndicator sentinelHealthIndicator( DefaultListableBeanFactory beanFactory, SentinelProperties sentinelProperties) { return new SentinelHealthIndicator(beanFactory, sentinelProperties); }
访问CURL端点

初始化原理
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

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); }

浙公网安备 33010602011771号