健康检查是生产级应用不可或缺的功能,SpringBoot通过HealthIndicator体系提供了强大的健康检查能力。本文将深入剖析HealthIndicator的聚合机制,从基础接口设计到复杂的聚合逻辑,全面解析SpringBoot如何管理、组织和聚合各类健康指标。通过本文,读者不仅能理解健康检查的核心实现原理,还能掌握如何扩展和定制健康检查系统。
// 健康指标基础接口
public interface HealthIndicator {
Health health();
}
// 健康信息封装类
public final class Health {
private final Status status;
private final Map details;
// 构建方法
public static Builder unknown() { /*...*/ }
public static Builder up() { /*...*/ }
public static Builder down() { /*...*/ }
public static Builder status(Status status) { /*...*/ }
// 状态枚举
public enum Status {
UP, DOWN, OUT_OF_SERVICE, UNKNOWN
}
// 构建器模式
public static class Builder {
public Builder withDetail(String key, Object value) { /*...*/ }
public Health build() { /*...*/ }
}
}
// 聚合健康指标接口
public interface HealthAggregator {
Health aggregate(Map healths);
}
健康检查体系
├── 核心接口
│ ├── HealthIndicator - 健康指标接口
│ ├── HealthAggregator - 聚合器接口
│ └── HealthEndpoint - 端点接口
│
├── 内置实现
│ ├── DiskSpaceHealthIndicator - 磁盘空间检查
│ ├── DataSourceHealthIndicator - 数据源检查
│ ├── RedisHealthIndicator - Redis检查
│ └── ...
│
├── 聚合机制
│ ├── OrderedHealthAggregator - 有序聚合
│ └── StatusAggregator - 状态聚合
│
└── 自动配置
├── HealthEndpointAutoConfiguration
└── HealthIndicatorAutoConfiguration
@Configuration(proxyBeanMethods = false)
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, RedisAutoConfiguration.class })
@EnableConfigurationProperties(HealthIndicatorProperties.class)
public class HealthIndicatorAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public DiskSpaceHealthIndicator diskSpaceHealthIndicator(HealthIndicatorProperties properties) {
DiskSpaceHealthIndicator indicator = new DiskSpaceHealthIndicator();
indicator.setThreshold(properties.getDiskSpace().getThreshold());
return indicator;
}
@Bean
@ConditionalOnBean(DataSource.class)
@ConditionalOnMissingBean(name = "dataSourceHealthIndicator")
public DataSourceHealthIndicator dataSourceHealthIndicator(DataSource dataSource) {
return new DataSourceHealthIndicator(dataSource);
}
// 其他健康指标的自动配置...
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnAvailableEndpoint(endpoint = HealthEndpoint.class)
public class HealthEndpointAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public HealthEndpoint healthEndpoint(HealthContributorRegistry registry,
HealthEndpointGroups groups) {
return new HealthEndpoint(registry, groups);
}
@Bean
@ConditionalOnMissingBean
public HealthContributorRegistry healthContributorRegistry(
Map contributors) {
return new AutoConfiguredHealthContributorRegistry(contributors);
}
@Bean
@ConditionalOnMissingBean
public HealthEndpointGroups healthEndpointGroups(HealthEndpointProperties properties) {
return new HealthEndpointGroups(properties);
}
}
public interface HealthContributorRegistry {
void registerContributor(String name, HealthContributor contributor);
HealthContributor unregisterContributor(String name);
HealthContributor getContributor(String name);
Iterator> iterator();
}
public class AutoConfiguredHealthContributorRegistry implements HealthContributorRegistry {
private final Map contributors = new LinkedHashMap<>();
@Override
public void registerContributor(String name, HealthContributor contributor) {
this.contributors.put(name, contributor);
}
@Override
public Health getHealth() {
Map healths = new LinkedHashMap<>();
for (Map.Entry entry : this.contributors.entrySet()) {
healths.put(entry.getKey(), getHealth(entry.getValue()));
}
return this.healthAggregator.aggregate(healths);
}
private Health getHealth(HealthContributor contributor) {
if (contributor instanceof HealthIndicator) {
return ((HealthIndicator) contributor).health();
}
if (contributor instanceof CompositeHealthContributor) {
return getCompositeHealth((CompositeHealthContributor) contributor);
}
return Health.unknown().build();
}
}
public interface CompositeHealthContributor extends HealthContributor {
HealthContributor getContributor(String name);
Iterator> iterator();
}
public class CompositeHealthIndicator implements CompositeHealthContributor {
private final HealthAggregator healthAggregator;
private final Map indicators = new LinkedHashMap<>();
public void addHealthIndicator(String name, HealthIndicator indicator) {
this.indicators.put(name, indicator);
}
@Override
public HealthContributor getContributor(String name) {
return this.indicators.get(name);
}
@Override
public Health health() {
Map healths = new LinkedHashMap<>();
for (Map.Entry entry : this.indicators.entrySet()) {
Health health = getHealth(entry.getValue());
if (health != null) {
healths.put(entry.getKey(), health);
}
}
return this.healthAggregator.aggregate(healths);
}
}
public class StatusAggregator implements HealthAggregator {
@Override
public Health aggregate(Map healths) {
Status status = aggregateStatus(healths.values());
Map details = new LinkedHashMap<>(healths);
return new Health.Builder(status, details).build();
}
private Status aggregateStatus(Collection healths) {
List candidates = new ArrayList<>();
for (Health health : healths) {
candidates.add(health.getStatus());
}
if (candidates.contains(Status.DOWN)) {
return Status.DOWN;
}
if (candidates.contains(Status.OUT_OF_SERVICE)) {
return Status.OUT_OF_SERVICE;
}
if (candidates.contains(Status.UNKNOWN)) {
return Status.UNKNOWN;
}
return Status.UP;
}
}
public class OrderedHealthAggregator implements HealthAggregator {
private final List order;
public OrderedHealthAggregator() {
this(DEFAULT_ORDER);
}
public OrderedHealthAggregator(List order) {
this.order = order;
}
@Override
public Health aggregate(Map healths) {
Status status = aggregateStatus(healths.values());
Map details = new LinkedHashMap<>(healths);
return new Health.Builder(status, details).build();
}
private Status aggregateStatus(Collection healths) {
for (Status candidate : this.order) {
for (Health health : healths) {
if (candidate.equals(health.getStatus())) {
return candidate;
}
}
}
return Status.UP;
}
}
public class HealthEndpoint {
private final HealthContributorRegistry registry;
private final HealthEndpointGroups groups;
public Health health() {
return healthForPath("");
}
public Health healthForPath(String path) {
HealthContributor contributor = getContributor(path);
if (contributor == null) {
return null;
}
return getHealth(contributor);
}
private Health getHealth(HealthContributor contributor) {
if (contributor instanceof HealthIndicator) {
return ((HealthIndicator) contributor).health();
}
if (contributor instanceof CompositeHealthContributor) {
return getCompositeHealth((CompositeHealthContributor) contributor);
}
return Health.unknown().build();
}
}
public class HealthEndpointGroups {
private final Map groups;
public HealthEndpointGroup getPrimary() {
return this.groups.get("");
}
public HealthEndpointGroup get(String name) {
return this.groups.get(name);
}
public boolean isMember(String name, HealthEndpointGroup group) {
return group.isMember(name);
}
}
public interface HealthEndpointGroup {
boolean isMember(String name);
boolean showComponents();
boolean showDetails();
StatusHttpMapper getStatusHttpMapper();
}
@Component
public class CustomHealthIndicator implements HealthIndicator {
@Override
public Health health() {
// 自定义健康检查逻辑
boolean isHealthy = checkServiceHealth();
if (isHealthy) {
return Health.up()
.withDetail("version", "1.0.0")
.withDetail("responseTime", "50ms")
.build();
} else {
return Health.down()
.withDetail("error", "Service unavailable")
.withDetail("retryAfter", "30s")
.build();
}
}
private boolean checkServiceHealth() {
// 实际健康检查逻辑
return true;
}
}
@Component
public class CustomHealthAggregator implements HealthAggregator {
@Override
public Health aggregate(Map healths) {
// 自定义聚合逻辑
boolean allUp = healths.values().stream()
.allMatch(health -> health.getStatus() == Status.UP);
Status status = allUp ? Status.UP : Status.DOWN;
Map details = new LinkedHashMap<>(healths);
return new Health.Builder(status, details)
.withDetail("timestamp", System.currentTimeMillis())
.build();
}
}
public class CachedHealthIndicator implements HealthIndicator {
private final HealthIndicator delegate;
private final long cacheDuration;
private volatile Health cachedHealth;
private volatile long lastChecked;
@Override
public Health health() {
long currentTime = System.currentTimeMillis();
if (currentTime > lastChecked + cacheDuration) {
synchronized (this) {
if (currentTime > lastChecked + cacheDuration) {
cachedHealth = delegate.health();
lastChecked = currentTime;
}
}
}
return cachedHealth;
}
}
public class AsyncHealthIndicator implements HealthIndicator {
private final HealthIndicator delegate;
private final Executor executor;
private volatile Health lastHealth;
@PostConstruct
public void init() {
executor.execute(() -> {
while (true) {
lastHealth = delegate.health();
try {
Thread.sleep(5000); // 5秒检查一次
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
});
}
@Override
public Health health() {
return lastHealth != null ? lastHealth : Health.unknown().build();
}
}
@Configuration
public class HealthSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/actuator/health").permitAll()
.antMatchers("/actuator/health/**").authenticated()
.and()
.httpBasic();
}
}
public class SensitiveHealthAggregator implements HealthAggregator {
private final HealthAggregator delegate;
private final SecurityContext securityContext;
@Override
public Health aggregate(Map healths) {
Health health = delegate.aggregate(healths);
if (!isAuthorized()) {
return Health.status(health.getStatus()).build();
}
return health;
}
private boolean isAuthorized() {
return securityContext.getAuthentication() != null
&& securityContext.getAuthentication().isAuthenticated();
}
}
SpringBoot的健康检查聚合机制体现了以下核心设计思想:
理解这一机制对于构建生产级应用至关重要,开发者能够:
SpringBoot的健康检查聚合机制是其Actuator模块的核心功能之一,掌握这一机制将显著提升应用的可靠性和可维护性。