说明
在容器化部署下,如何判断服务是否健康通过 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;}@Overrideprotected 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// detailif (!sentinelProperties.isEnabled()) {detailMap.put("enabled", false);builder.up().withDetails(detailMap);return;}detailMap.put("enabled", true);// Check health of Dashboardboolean 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// resultHeartbeatSender 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 DOWNdashboardUp = 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 DataSourceboolean 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 okayMap<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// DOWNdataSourceUp = false;dataSourceDetailMap.put(dataSourceBeanName,new Status(Status.UNKNOWN.getCode(), e.getMessage()));}}// If Dashboard and DataSource are both OK, the health status is UPif (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();//构建 HealthIndicatorRegistryreturn 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为sentinelString 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
@ReadOperationpublic 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
@Overridepublic 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);}