前言

spring boot actuator中的HealthEndPoint涉及的内容比较多, HealthEndPoint是通过HealthIndicator来实现功能的,而HealthIndicator的实现都在org.springframework.boot.actuate.health包下,如图:

springboot 健康检查地址 springboot health_springboot 健康检查地址

整理成类图如下:

springboot 健康检查地址 springboot health_spring_02

本节我们就来分析这部分的内容.

解析

HealthIndicator

HealthIndicator 是一个顶层接口,在之前的类图我们可以知道,其只声明了1个方法,如下:

public interface HealthIndicator {

    // 返回health
    Health health();

}

这里面涉及到了Health, 该类的实现使用了建造者模式,类图如下:

springboot 健康检查地址 springboot health_spring_03

  1. Status 在类上声明了如下注解:
@JsonInclude(Include.NON_EMPTY)

则如果该类中的所有属性都为空(“”)或者为 NULL则不序列化.

其内部定义了如下字段:

// 状态码
private final String code;

// 描述
private final String description;

并预定义了4个静态字段,:

// 系统状态未知
public static final Status UNKNOWN = new Status("UNKNOWN");

// 可用
public static final Status UP = new Status("UP");

// 服务挂掉
public static final Status DOWN = new Status("DOWN");

// 服务不可用
public static final Status OUT_OF_SERVICE = new Status("OUT_OF_SERVICE");
  1. Health 类同样也声明了@JsonInclude(Include.NON_EMPTY) 注解,表明如果该类中所有属性如果为空(“”) 或者为NULL则不序列化.
    声明了如下字段:
// 状态
private final Status status;

// 详情
private final Map<String, Object> details;
  1. Builder 则是定义在 Health类中,则定义的字段和Health中的一样.则构造器如下:
public Builder() {
        this.status = Status.UNKNOWN;
        this.details = new LinkedHashMap<String, Object>();
}

意味着:默认情况下,构建出来的Health状态为UNKNOWN.

  1. 如何使用 Builder 构建Health呢?
    可以通过如下的方式进行:
try {
// do some test to determine state of component
return new Health.Builder().up().withDetail("version", "1.1.2").build();
}
catch (Exception ex) {
return new Health.Builder().down(ex).build();
 }

更多的方式,可以参考Builder类的api

AbstractHealthIndicator

spring的一贯套路是 定义1个接口,然后声明1个抽象实现,然后定义1堆实现继承抽象类(模板方法).对于HealthIndicator来说,也是同样.定义了AbstractHealthIndicator,实现了health方法,同时声明了doHealthCheck方法,让子类进行实现:

public final Health health() {
        // 1. 实例化Health$Builder
        Health.Builder builder = new Health.Builder();
        try {
            // 2. 进行状态的检查,如果在检查过程中出现异常,则状态为Status.DOWN
            doHealthCheck(builder);
        }
        catch (Exception ex) {
            this.logger.warn("Health check failed", ex);
            builder.down(ex);
        }
        // 3. 构建Health
        return builder.build();
    }
  1. 实例化Health$Builder
  2. 调用抽象方法doHealthCheck–>进行状态的检查,如果在检查过程中出现异常,则状态为Status.DOWN
  3. 构建Health

下面我们就依次看一下其各个子类的实现吧

ApplicationHealthIndicator

  1. 该类的实现比较简单,通过继承AbstractHealthIndicator,实现其doHealthCheck,在该方法中直接将其设置为up.代码如下:
protected void doHealthCheck(Health.Builder builder) throws Exception {
        builder.up();
    }
  1. 自动化配置:
    在HealthIndicatorAutoConfiguration 进行了配置,代码如下:
@Bean
@ConditionalOnMissingBean(HealthIndicator.class)
public ApplicationHealthIndicator applicationHealthIndicator() {
    return new ApplicationHealthIndicator();
}
  • @Bean –> 注册1个id为applicationHealthIndicator,类型为ApplicationHealthIndicator的bean
  • @ConditionalOnMissingBean(HealthIndicator.class) –> 当beanFactory中不存在HealthIndicator类型的bean时生效

CassandraHealthIndicator

  1. CassandraHealthIndicator 持有1个CassandraOperations类型的实例,该实例通过构造器进行注入,代码如下:
private CassandraOperations cassandraOperations;

public CassandraHealthIndicator(CassandraOperations cassandraOperations) {
    Assert.notNull(cassandraOperations, "CassandraOperations must not be null");
    this.cassandraOperations = cassandraOperations;
}
  1. doHealthCheck 实现:
protected void doHealthCheck(Health.Builder builder) throws Exception {
    try {
        Select select = QueryBuilder.select("release_version").from("system",
                "local");
        ResultSet results = this.cassandraOperations.query(select);
        if (results.isExhausted()) {
            builder.up();
            return;
        }
        String version = results.one().getString(0);
        builder.up().withDetail("version", version);
    }
    catch (Exception ex) {
        builder.down(ex);
    }
}
  1. 通过CassandraOperations 查询 在system(keyspace)下的local表中的 release_version 字段
  2. 如果执行成功,但是没有返回值,则设置为up,然后return
  3. 否则,设置状态为up,并添加key–>version,value–> 结果值到详情中
  4. 如果在查询过程中出现异常,则设置为状态为down.
  1. 自动化装配:
    定义在CassandraHealthIndicatorConfiguration中,该类继承了CompositeHealthIndicatorConfiguration, CompositeHealthIndicatorConfiguration 声明如下:
public abstract class CompositeHealthIndicatorConfiguration<H extends HealthIndicator, S>

其中,泛型参数H 代表 HealthIndicator 的类型,泛型参数S代表bean的原始类型(the bean source type).

在该类中声明了2个方法:

  1. createHealthIndicator,代码如下:
protected HealthIndicator createHealthIndicator(Map<String, S> beans) {
if (beans.size() == 1) {
    return createHealthIndicator(beans.values().iterator().next());
}
CompositeHealthIndicator composite = new CompositeHealthIndicator(
        this.healthAggregator);
for (Map.Entry<String, S> entry : beans.entrySet()) {
    composite.addHealthIndicator(entry.getKey(),
            createHealthIndicator(entry.getValue()));
}
    return composite;
}
  1. 如果传入的beans的size 为 1,则调用createHealthIndicator 创建HealthIndicator
  2. 否则,创建CompositeHealthIndicator,遍历传入的beans,依次创建HealthIndicator,加入到CompositeHealthIndicator中
  1. createHealthIndicator,代码如下:
protected H createHealthIndicator(S source) {
Class<?>[] generics = ResolvableType
        .forClass(CompositeHealthIndicatorConfiguration.class, getClass())
        .resolveGenerics();
Class<H> indicatorClass = (Class<H>) generics[0];
Class<S> sourceClass = (Class<S>) generics[1];
try {
    return indicatorClass.getConstructor(sourceClass).newInstance(source);
}
catch (Exception ex) {
    throw new IllegalStateException("Unable to create indicator " + indicatorClass
            + " for source " + sourceClass, ex);
}
}
  1. 获得CompositeHealthIndicatorConfiguration中的泛型参数
  2. 根据泛型参数H对应的class和S对应的class,在H对应的class中找到声明了参数为S类型的构造器进行实例化.
  1. 由于CassandraHealthIndicatorConfiguration声明了如下注解:
@Configuration
@ConditionalOnClass({ CassandraOperations.class, Cluster.class })
@ConditionalOnBean(CassandraOperations.class)
@ConditionalOnEnabledHealthIndicator("cassandra")
  • @Configuration –> 配置类
  • @ConditionalOnClass({ CassandraOperations.class, Cluster.class }) –> 在当前的类路径下存在CassandraOperations.class, Cluster.class时生效
  • @ConditionalOnBean(CassandraOperations.class)–> 在beanFactory 中存在CassandraOperations类型的bean时生效
  • @ConditionalOnEnabledHealthIndicator(“cassandra”)–> 如果配置有management.health.cassandra.enabled = true 或者management.health.defaults.enabled = true 则该配置生效.关于此处的实现我们后续有文章进行分析.

因此,默认情况下,该配置(CassandraHealthIndicator) 是不会生效的.

  1. 同时,在@Bean方法上声明了如下注解:
@Bean
@ConditionalOnMissingBean(name = "cassandraHealthIndicator")
public HealthIndicator cassandraHealthIndicator() {
    return createHealthIndicator(this.cassandraOperations);
}
  • @Bean –> 注册1个id为cassandraHealthIndicator,类型为HealthIndicator的bean
  • @ConditionalOnMissingBean(name = “cassandraHealthIndicator”) –> 当beanFactory中不存在id为cassandraHealthIndicator 的bean 时生效.

由于CassandraHealthIndicatorConfiguration的声明如下:

public static class CassandraHealthIndicatorConfiguration extends
        CompositeHealthIndicatorConfiguration<CassandraHealthIndicator, CassandraOperations>

因此,H为 CassandraHealthIndicator,S 为 CassandraOperations.

又由于CassandraHealthIndicatorConfiguration 持有的是Map 集合,因此,在实例化CassandraHealthIndicatorConfiguration的时候,会将BeanFactory中所有类型为CassandraOperations都注入进来,key–>bean的id,value –> bean的实例.

因此,当前创建的HealthIndicator有2种情况:

  1. 当前beanFactory中只存在1个CassandraOperations的实例:
    则此时会调用createHealthIndicator 进行创建.首先获得CassandraHealthIndicator类中声明的参数为CassandraOperations的构造器,如下:
public CassandraHealthIndicator(CassandraOperations cassandraOperations) {
Assert.notNull(cassandraOperations, "CassandraOperations must not be null");
this.cassandraOperations = cassandraOperations;
}

然后进行实例化.

  1. 当前beanFactory中存在多个CassandraOperations实例的话,则会创建CompositeHealthIndicator,会遍历CassandraOperations的实例,创建CassandraHealthIndicator,在CompositeHealthIndicator中进行注册,其中,Key为CassandraOperations bean的id,value为 CassandraHealthIndicator

CouchbaseHealthIndicator

  1. CouchbaseHealthIndicator和CassandraHealthIndicator类似,其内部持有CassandraOperations,构造器如下:
private CassandraOperations cassandraOperations;


public CassandraHealthIndicator(CassandraOperations cassandraOperations) {
    Assert.notNull(cassandraOperations, "CassandraOperations must not be null");
    this.cassandraOperations = cassandraOperations;
}
  1. doHealthCheck 实现如下:
protected void doHealthCheck(Health.Builder builder) throws Exception {
    List<Version> versions = this.couchbaseOperations.getCouchbaseClusterInfo()
            .getAllVersions();
    builder.up().withDetail("versions",
            StringUtils.collectionToCommaDelimitedString(versions));
}
  1. 通过couchbaseOperations 获得版本号
  2. 如果访问成功的话,则设置状态为up,并设置key–> versions,value–> 版本号,如果有多个的话,会通过,进行拼接
  1. 自动装配:
    同样和CassandraHealthIndicatorConfiguration 一样,继承自CompositeHealthIndicatorConfiguration,泛型参数分别为CouchbaseHealthIndicator, CouchbaseOperations,代码如下:
public static class CouchbaseHealthIndicatorConfiguration extends
        CompositeHealthIndicatorConfiguration<CouchbaseHealthIndicator, CouchbaseOperations>
  1. 由于CouchbaseHealthIndicatorConfiguration 声明有如下注解:
@Configuration
@ConditionalOnClass({ CouchbaseOperations.class, Bucket.class })
@ConditionalOnBean(CouchbaseOperations.class)
@ConditionalOnEnabledHealthIndicator("couchbase")
  • @Configuration –> 配置类
  • @ConditionalOnClass({ CouchbaseOperations.class, Bucket.class }) –> 在当前类路径下存在CouchbaseOperations.class, Bucket.class时生效
  • @ConditionalOnBean(CouchbaseOperations.class)–> 在beanFactory中存在CouchbaseOperations类型的bean时生效
  • @ConditionalOnEnabledHealthIndicator(“couchbase”) –> 如果配置有management.health.couchbase.enabled = true 或者management.health.defaults.enabled = true 则该配置生效.
  1. 其@Bean方法如下:
@Bean
@ConditionalOnMissingBean(name = "couchbaseHealthIndicator")
public HealthIndicator couchbaseHealthIndicator() {
    return createHealthIndicator(this.couchbaseOperations);
}
  • @Bean –> 注册1个id为couchbaseHealthIndicator,类型为HealthIndicator的bean
  • @ConditionalOnMissingBean(name = “couchbaseHealthIndicator”) –> 当beanFactory中不存在id为couchbaseHealthIndicator 的bean 时生效.

其构建过程和CassandraHealthIndicatorConfiguration一样,这里就不在赘述了

DataSourceHealthIndicator

  1. DataSourceHealthIndicator 中定义了如下字段:
// 默认的测试语句为SELECT 1
private static final String DEFAULT_QUERY = "SELECT 1";

// 数据库资源
private DataSource dataSource;

// 测试语句
private String query;

private JdbcTemplate jdbcTemplate;
  1. DataSourceHealthIndicator 实现了InitializingBean接口,因此在初始化后会调用afterPropertiesSet方法,代码如下:
public void afterPropertiesSet() throws Exception {
    Assert.state(this.dataSource != null,
            "DataSource for DataSourceHealthIndicator must be specified");
}
  1. doHealthCheck 实现:
protected void doHealthCheck(Health.Builder builder) throws Exception {
    // 1. 如果DataSource没有配置,则直接返回up,message 为unknown
    if (this.dataSource == null) {
        builder.up().withDetail("database", "unknown");
    }
    else {
        // 2. 
        doDataSourceHealthCheck(builder);
    }
}
  1. 如果DataSource没有配置,则直接返回up,message 为unknown
  2. 否则,调用doDataSourceHealthCheck,代码如下:
private void doDataSourceHealthCheck(Health.Builder builder) throws Exception {
// 1. 获得数据库产商,并添加至builder中
String product = getProduct();
builder.up().withDetail("database", product);
// 2. 获得测试的语句,默认为SELECT 1,然后进行查询,将查询结果放入builder中,key-->hello,value-->结果值
// 如果出现异常,则调用Builder#down
String validationQuery = getValidationQuery(product);
if (StringUtils.hasText(validationQuery)) {
    try {
        // Avoid calling getObject as it breaks MySQL on Java 7
        List<Object> results = this.jdbcTemplate.query(validationQuery,
                new SingleColumnRowMapper());
        Object result = DataAccessUtils.requiredSingleResult(results);
        builder.withDetail("hello", result);
    }
    catch (Exception ex) {
        builder.down(ex);
    }
}
}
  1. 获得数据库产商,并添加至builder中,代码如下:
private String getProduct() {
return this.jdbcTemplate.execute(new ConnectionCallback<String>() {
@Override
public String doInConnection(Connection connection)
        throws SQLException, DataAccessException {
    return connection.getMetaData().getDatabaseProductName();
}
});
}
  1. 获得测试的语句,默认为SELECT 1,然后进行查询,将查询结果放入builder中,key–>hello,value–>结果值,如果出现异常,则调用Builder#down 设置状态为down.获得查询语句的代码如下:
protected String getValidationQuery(String product) {
// 1. 将配置的query 赋值为query
String query = this.query;
// 2. 如果没有配置的话,则通过DatabaseDriver获得
if (!StringUtils.hasText(query)) {
DatabaseDriver specific = DatabaseDriver.fromProductName(product);
query = specific.getValidationQuery();
}
// 3. 如果还没有配置的化,则返回默认的SELECT 1
if (!StringUtils.hasText(query)) {
query = DEFAULT_QUERY;
}
return query;
}
  1. 自动装配:
  1. 定义在DataSourcesHealthIndicatorConfiguration中,该类继承了CompositeHealthIndicatorConfiguration,泛型参数为DataSourceHealthIndicator, DataSource,同时实现了InitializingBean 接口.故会在初始化调用其afterPropertiesSet方法,代码如下:
public void afterPropertiesSet() throws Exception {
    this.poolMetadataProvider = new DataSourcePoolMetadataProviders(
            this.metadataProviders);
}

实例化了 DataSourcePoolMetadataProviders,该类实现了DataSourcePoolMetadataProvider,封装了数据库的元数据.关于这部分的代码我们在下篇文章进行分析

  1. DataSourcesHealthIndicatorConfiguration 声明了如下注解:
@Configuration
@ConditionalOnClass({ JdbcTemplate.class, AbstractRoutingDataSource.class })
@ConditionalOnBean(DataSource.class)
@ConditionalOnEnabledHealthIndicator("db")
  • @Configuration –> 配置类
  • @ConditionalOnClass({ JdbcTemplate.class, AbstractRoutingDataSource.class }) –> 在类路径下存在 JdbcTemplate.class, AbstractRoutingDataSource.class 时生效
  • @ConditionalOnBean(DataSource.class) –> 在BeanFactory中存在DataSource类型的bean时生效
  • @ConditionalOnEnabledHealthIndicator(“db”)–> 如果配置有management.health.db.enabled = true 或者management.health.defaults.enabled = true 则该配置生效.
  1. 在@Bean方法中声明了如下注解:
@Bean
@ConditionalOnMissingBean(name = "dbHealthIndicator")
public HealthIndicator dbHealthIndicator() {
    return createHealthIndicator(this.dataSources);
}
  • @Bean –> 注册1个id为dbHealthIndicator,类型为HealthIndicator的bean
  • @ConditionalOnMissingBean(name = “dbHealthIndicator”) –> 当beanFactory中不存在id为dbHealthIndicator 的bean 时生效.
  1. 由于DataSourcesHealthIndicatorConfiguration覆写了createHealthIndicator,因此在创建DataSourceHealthIndicator 时,会执行如下代码:
protected DataSourceHealthIndicator createHealthIndicator(DataSource source) {
    return new DataSourceHealthIndicator(source, getValidationQuery(source));
}

直接实例化DataSourceHealthIndicator,检查sql语句通过getValidationQuery来进行获取,代码如下:

private String getValidationQuery(DataSource source) {
    DataSourcePoolMetadata poolMetadata = this.poolMetadataProvider
            .getDataSourcePoolMetadata(source);
    return (poolMetadata == null ? null : poolMetadata.getValidationQuery());
}
  1. 如果DataSourcePoolMetadata 存在,则获得DataSourcePoolMetadata中配置的检查sql,此时分2种情况,如果配置了sql,则进行检查时,执行的是配置的sql语句,否则,使用的是默认的sql–> Select 1
  2. 否则,返回null,此时使用的是默认的sql–>Select 1

LdapHealthIndicator

  1. LdapHealthIndicator定义了如下字段:
private static final ContextExecutor<String> versionContextExecutor = new VersionContextExecutor();

private final LdapOperations ldapOperations;

其中VersionContextExecutor 会在doHealthCheck 中用到,其实现了org.springframework.ldap.core.ContextExecutor 接口,代码如下:

private static class VersionContextExecutor implements ContextExecutor<String> {

    @Override
    public String executeWithContext(DirContext ctx) throws NamingException {
        Object version = ctx.getEnvironment().get("java.naming.ldap.version");
        if (version != null) {
            return (String) version;
        }
        return null;
    }

}
  1. doHealthCheck 实现:
protected void doHealthCheck(Health.Builder builder) throws Exception {
    // 1. 通过LdapOperations 来获取java.naming.ldap.version的值.如果获取不到,则返回null
    String version = this.ldapOperations.executeReadOnly(versionContextExecutor);
    // 2. 设置为up,属性值 version --> 版本号
    builder.up().withDetail("version", version);
}
  1. 通过LdapOperations 来获取java.naming.ldap.version的值.如果获取不到,则返回null
  2. 如果版本号不等于null,则设置为up,属性值 version –> 版本号,否则,设置为down(在withDetail中对value进行了断言,此时传入null,导致断言失败,抛出异常,因此会设置为down)
  1. 自动装配:
  1. 在LdapHealthIndicatorConfiguration中声明,该类继承了CompositeHealthIndicatorConfiguration,泛型参数分别为LdapHealthIndicator, LdapOperations
  2. LdapHealthIndicatorConfiguration 声明了如下注解:
@Configuration
@ConditionalOnClass(LdapOperations.class)
@ConditionalOnBean(LdapOperations.class)
@ConditionalOnEnabledHealthIndicator("ldap")
  • @Configuration –> 配置类
  • @ConditionalOnClass({LdapOperations.class }) –> 在类路径下存在 LdapOperations.class 时生效
  • @ConditionalOnBean(LdapOperations.class) –> 在BeanFactory中存在LdapOperations类型的bean时生效
  • @ConditionalOnEnabledHealthIndicator(“ldap”)–> 如果配置有management.health.ldap.enabled = true 或者management.health.defaults.enabled = true 则该配置生效.
  1. @Bean方法中声明了如下注解:
@Bean
@ConditionalOnMissingBean(name = "ldapHealthIndicator")
public HealthIndicator ldapHealthIndicator() {
    return createHealthIndicator(this.ldapOperations);
}
  • @Bean –> 注册1个id为ldapHealthIndicator,类型为HealthIndicator的bean
  • @ConditionalOnMissingBean(name = “ldapHealthIndicator”) –> 当beanFactory中不存在id为ldapHealthIndicator 的bean 时生效.

创建LdapHealthIndicator的过程和CassandraHealthIndicatorConfiguration一样,这里就不在赘述了.

MongoHealthIndicator

  1. MongoHealthIndicator 继承了AbstractHealthIndicator,有如下字段:
private final MongoTemplate mongoTemplate;

构造器为:

public MongoHealthIndicator(MongoTemplate mongoTemplate) {
    Assert.notNull(mongoTemplate, "MongoTemplate must not be null");
    this.mongoTemplate = mongoTemplate;
}
  1. doHealthCheck 的实现很简单,通过MongoTemplate 执行{ buildInfo: 1 },如果成功的话,则设置为up并设置属性version–> 版本号,否则,设置为down.代码如下:
protected void doHealthCheck(Health.Builder builder) throws Exception {
    CommandResult result = this.mongoTemplate.executeCommand("{ buildInfo: 1 }");
    builder.up().withDetail("version", result.getString("version"));
}
  1. 自动装配:
  1. 在MongoHealthIndicatorConfiguration中定义,继承了CompositeHealthIndicatorConfiguration,泛型参数为MongoHealthIndicator, MongoTemplate
  2. MongoHealthIndicatorConfiguration 声明了如下注解:
@Configuration
@ConditionalOnClass(MongoTemplate.class)
@ConditionalOnBean(MongoTemplate.class)
@ConditionalOnEnabledHealthIndicator("mongo")
  • @Configuration –> 配置类
  • @ConditionalOnClass({MongoTemplate.class }) –> 在类路径下存在 MongoTemplate.class 时生效
  • @ConditionalOnBean(MongoTemplate.class) –> 在BeanFactory中存在MongoTemplate类型的bean时生效
  • @ConditionalOnEnabledHealthIndicator(“mongo”)–> 如果配置有management.health.mongo.enabled = true 或者management.health.defaults.enabled = true 则该配置生效.
  1. @Bean方法 如下:
@Bean
@ConditionalOnMissingBean(name = "mongoHealthIndicator")
public HealthIndicator mongoHealthIndicator() {
    return createHealthIndicator(this.mongoTemplates);
}
  • @Bean –> 注册1个id为mongoHealthIndicator,类型为HealthIndicator的bean
  • @ConditionalOnMissingBean(name = “mongoHealthIndicator”) –> 当beanFactory中不存在id为mongoHealthIndicator 的bean 时生效.

RedisHealthIndicator

  1. RedisHealthIndicator 定义了如下字段:
private static final String VERSION = "version";

private static final String REDIS_VERSION = "redis_version";

private final RedisConnectionFactory redisConnectionFactory;

构造器如下:

public RedisHealthIndicator(RedisConnectionFactory connectionFactory) {
    Assert.notNull(connectionFactory, "ConnectionFactory must not be null");
    this.redisConnectionFactory = connectionFactory;
}
  1. doHealthCheck 代码如下:
protected void doHealthCheck(Health.Builder builder) throws Exception {
    // 1. 获得redis链接
    RedisConnection connection = RedisConnectionUtils
            .getConnection(this.redisConnectionFactory);
    try {
        if (connection instanceof RedisClusterConnection) {
            // 2.1 如果是集群的话,则获得集群的信息.并设置状态为up,属性值cluster_size-->集群节点数量
            // slots_up --> 可用槽的数量, slots_fail --> 不可用槽的数量
            ClusterInfo clusterInfo = ((RedisClusterConnection) connection)
                    .clusterGetClusterInfo();
            builder.up().withDetail("cluster_size", clusterInfo.getClusterSize())
                    .withDetail("slots_up", clusterInfo.getSlotsOk())
                    .withDetail("slots_fail", clusterInfo.getSlotsFail());
        }
        else {
            // 2.2 如果不是集群的话,则直接获取版本号
            Properties info = connection.info();
            builder.up().withDetail(VERSION, info.getProperty(REDIS_VERSION));
        }
    }
    finally {
        // 3. 释放链接
        RedisConnectionUtils.releaseConnection(connection,
                this.redisConnectionFactory);
    }
}
  1. 获得redis链接
  2. 如果是集群的话,则获得集群的信息.并设置状态为up,属性值cluster_size–>集群节点数量,slots_up –> 可用槽的数量, slots_fail –> 不可用槽的数量
  3. 如果不是集群的话,则直接获取版本号
  4. 释放链接
  1. 自动装配:
  1. 在RedisHealthIndicatorConfiguration中定义,继承了CompositeHealthIndicatorConfiguration,泛型参数分别为RedisHealthIndicator, RedisConnectionFactory
  2. RedisHealthIndicatorConfiguration 声明了如下注解:
@Configuration
@ConditionalOnClass(RedisConnectionFactory.class)
@ConditionalOnBean(RedisConnectionFactory.class)
@ConditionalOnEnabledHealthIndicator("redis")
  • @Configuration –> 配置类
  • @ConditionalOnClass({RedisConnectionFactory.class }) –> 在类路径下存在 RedisConnectionFactory.class 时生效
  • @ConditionalOnBean(RedisConnectionFactory.class) –> 在BeanFactory中存在RedisConnectionFactory类型的bean时生效
  • @ConditionalOnEnabledHealthIndicator(“redis”)–> 如果配置有management.health.redis.enabled = true 或者management.health.defaults.enabled = true 则该配置生效.
  1. @Bean方法:
@Bean
@ConditionalOnMissingBean(name = "redisHealthIndicator")
public HealthIndicator redisHealthIndicator() {
    return createHealthIndicator(this.redisConnectionFactories);
}
  • @Bean –> 注册1个id为redisHealthIndicator,类型为HealthIndicator的bean
  • @ConditionalOnMissingBean(name = “redisHealthIndicator”) –> 当beanFactory中不存在id为redisHealthIndicator 的bean 时生效.

RabbitHealthIndicator

  1. RabbitHealthIndicator 继承AbstractHealthIndicator,声明了如下字段:
private final RabbitTemplate rabbitTemplate;

构造器如下:

public RabbitHealthIndicator(RabbitTemplate rabbitTemplate) {
    Assert.notNull(rabbitTemplate, "RabbitTemplate must not be null.");
    this.rabbitTemplate = rabbitTemplate;
}
  1. doHealthCheck–>通过rabbitTemplate 查询版本号,如果查询成功的话,则设置状态为up,并设置属性值version–>RabbitMQ的版本号, 代码如下:
protected void doHealthCheck(Health.Builder builder) throws Exception {
    builder.up().withDetail("version", getVersion());
}

其中getVersion 的代码如下:

private String getVersion() {
    return this.rabbitTemplate.execute(new ChannelCallback<String>() {
        @Override
        public String doInRabbit(Channel channel) throws Exception {
            Map<String, Object> serverProperties = channel.getConnection()
                    .getServerProperties();
            return serverProperties.get("version").toString();
        }
    });
}
  1. 自动装配:
  1. 在RabbitHealthIndicatorConfiguration中,继承了CompositeHealthIndicatorConfiguration,泛型参数分别为RabbitHealthIndicator, RabbitTemplate.
  2. RabbitHealthIndicatorConfiguration 声明了如下注解:
@Configuration
@ConditionalOnClass(RabbitTemplate.class)
@ConditionalOnBean(RabbitTemplate.class)
@ConditionalOnEnabledHealthIndicator("rabbit")
  • @Configuration –> 配置类
  • @ConditionalOnClass({RabbitTemplate.class }) –> 在类路径下存在 RabbitTemplate.class 时生效
  • @ConditionalOnBean(RabbitTemplate.class) –> 在BeanFactory中存在RabbitTemplate类型的bean时生效
  • @ConditionalOnEnabledHealthIndicator(“rabbit”)–> 如果配置有management.health.rabbit.enabled = true 或者management.health.defaults.enabled = true 则该配置生效.
  1. @Bean方法如下:
@Bean
@ConditionalOnMissingBean(name = "rabbitHealthIndicator")
public HealthIndicator rabbitHealthIndicator() {
    return createHealthIndicator(this.rabbitTemplates);
}
  • @Bean –> 注册1个id为rabbitHealthIndicator,类型为HealthIndicator的bean
  • @ConditionalOnMissingBean(name = “rabbitHealthIndicator”) –> 当beanFactory中不存在id为rabbitHealthIndicator 的bean 时生效.

SolrHealthIndicator

  1. SolrHealthIndicator 继承自AbstractHealthIndicator,声明字段如下:
private final SolrClient solrClient;

构造器如下:

public SolrHealthIndicator(SolrClient solrClient) {
    this.solrClient = solrClient;
}
  1. doHealthCheck,通过SolrClient 来获取solr的状态,如果返回的状态码为0,则设置为up,属性值为solrStatus–>OK,否则,设置为down,属性值为solrStatus–>返回的状态值,代码如下:
protected void doHealthCheck(Health.Builder builder) throws Exception {
    CoreAdminRequest request = new CoreAdminRequest();
    request.setAction(CoreAdminParams.CoreAdminAction.STATUS);
    CoreAdminResponse response = request.process(this.solrClient);
    int statusCode = response.getStatus();
    Status status = (statusCode == 0 ? Status.UP : Status.DOWN);
    builder.status(status).withDetail("solrStatus",
            (statusCode == 0 ? "OK" : statusCode));
}
  1. 自动装配:
  1. 在SolrHealthIndicatorConfiguration中,继承自CompositeHealthIndicatorConfiguration,泛型参数分别为SolrHealthIndicator, SolrClient
  2. SolrHealthIndicatorConfiguration 声明了如下注解:
@Configuration
@ConditionalOnClass(SolrClient.class)
@ConditionalOnBean(SolrClient.class)
@ConditionalOnEnabledHealthIndicator("solr")
  • @Configuration –> 配置类
  • @ConditionalOnClass({SolrClient.class }) –> 在类路径下存在 SolrClient.class 时生效
  • @ConditionalOnBean(SolrClient.class) –> 在BeanFactory中存在SolrClient类型的bean时生效
  • @ConditionalOnEnabledHealthIndicator(“solr”)–> 如果配置有management.health.solr.enabled = true 或者management.health.defaults.enabled = true 则该配置生效.
  1. @Bean方法:
@Bean
@ConditionalOnMissingBean(name = "solrHealthIndicator")
public HealthIndicator solrHealthIndicator() {
    return createHealthIndicator(this.solrClients);
}
  • @Bean –> 注册1个id为solrHealthIndicator,类型为HealthIndicator的bean
  • @ConditionalOnMissingBean(name = “solrHealthIndicator”) –> 当beanFactory中不存在id为solrHealthIndicator 的bean 时生效.

MailHealthIndicator

  1. MailHealthIndicator 继承自AbstractHealthIndicator,其内部持有JavaMailSenderImpl的实例,构造器如下:
public MailHealthIndicator(JavaMailSenderImpl mailSender) {
    this.mailSender = mailSender;
}
  1. doHealthCheck 实现如下:
protected void doHealthCheck(Builder builder) throws Exception {
    // 1. 设置属性location-->mailSender配置的主机名:mailSender配置的端口号
    builder.withDetail("location",
            this.mailSender.getHost() + ":" + this.mailSender.getPort());
    // 2. 测试链接,如果成功,则状态为up,否则为down
    this.mailSender.testConnection();
    builder.up();
}
  1. 设置属性location–>mailSender配置的主机名:mailSender配置的端口号
  2. 测试链接,如果成功,则状态为up,否则为down
  1. 自动装配:
  1. 声明在MailHealthIndicatorConfiguration,该类继承自CompositeHealthIndicatorConfiguration,泛型参数为MailHealthIndicator, JavaMailSenderImpl
  2. MailHealthIndicatorConfiguration声明了如下注解:
@Configuration
@ConditionalOnClass(JavaMailSenderImpl.class)
@ConditionalOnBean(JavaMailSenderImpl.class)
@ConditionalOnEnabledHealthIndicator("mail")
  • @Configuration –> 配置类
  • @ConditionalOnClass({JavaMailSenderImpl.class }) –> 在类路径下存在 JavaMailSenderImpl.class 时生效
  • @ConditionalOnBean(JavaMailSenderImpl.class) –> 在BeanFactory中存在JavaMailSenderImpl类型的bean时生效
  • @ConditionalOnEnabledHealthIndicator(“mail”)–> 如果配置有management.health.mail.enabled = true 或者management.health.defaults.enabled = true 则该配置生效.
  1. @Bean方法如下:
@Bean
@ConditionalOnMissingBean(name = "mailHealthIndicator")
public HealthIndicator mailHealthIndicator() {
    return createHealthIndicator(this.mailSenders);
}
  • @Bean –> 注册1个id为mailHealthIndicator,类型为HealthIndicator的bean
  • @ConditionalOnMissingBean(name = “mailHealthIndicator”) –> 当beanFactory中不存在id为mailHealthIndicator 的bean 时生效.

JmsHealthIndicator

  1. JmsHealthIndicator 继承自AbstractHealthIndicator,其内部持有ConnectionFactory,通过构造器注入,代码如下:
public JmsHealthIndicator(ConnectionFactory connectionFactory) {
    this.connectionFactory = connectionFactory;
}
  1. doHealthCheck–>检查方式为通过ConnectionFactory 来创建1个Connection,并调用其start 方法,如果正常的话,则设置为状态为up,否则设置为down(父类方法中的try-catch设置),检查完毕后,将Connection进行关闭(finally 块保证).代码如下:
protected void doHealthCheck(Health.Builder builder) throws Exception {
    Connection connection = this.connectionFactory.createConnection();
    try {
        connection.start();
        builder.up().withDetail("provider",
                connection.getMetaData().getJMSProviderName());
    }
    finally {
        connection.close();
    }
}
  1. 自动装配:
  1. 声明在JmsHealthIndicatorConfiguration中,继承自CompositeHealthIndicatorConfiguration,泛型参数分别为JmsHealthIndicator, ConnectionFactory
  2. JmsHealthIndicatorConfiguration 声明了如下注解:
@Configuration
@ConditionalOnClass(ConnectionFactory.class)
@ConditionalOnBean(ConnectionFactory.class)
@ConditionalOnEnabledHealthIndicator("jms")
  • @Configuration –> 配置类
  • @ConditionalOnClass({ConnectionFactory.class }) –> 在类路径下存在 ConnectionFactory.class 时生效
  • @ConditionalOnBean(ConnectionFactory.class) –> 在BeanFactory中存在ConnectionFactory类型的bean时生效
  • @ConditionalOnEnabledHealthIndicator(“jms”)–> 如果配置有management.health.jms.enabled = true 或者management.health.defaults.enabled = true 则该配置生效.
  1. @Bean方法如下:
@Bean
@ConditionalOnMissingBean(name = "jmsHealthIndicator")
public HealthIndicator jmsHealthIndicator() {
    return createHealthIndicator(this.connectionFactories);
}
  • @Bean –> 注册1个id为jmsHealthIndicator,类型为HealthIndicator的bean
  • @ConditionalOnMissingBean(name = “jmsHealthIndicator”) –> 当beanFactory中不存在id为jmsHealthIndicator 的bean 时生效.

这里提1个问题,为什么 Cassandra,Couchbase,DataSource 等的监控是通过继承CompositeHealthIndicatorConfiguration实现的?

答案: 因为在sping 的应用中,关于这些资源的配置可能会配置多个,那么此时就需要分别做处理:

  1. 如果配置1个则直接实例化对应的HealthIndicator即可
  2. 否则,就实例化CompositeHealthIndicator,对配置的资源的健康情况做监控,最后给出一个聚合的结果(后面有详解)

同样,资源有很多,我们如果每个资源都写一套逻辑的话,就太Low了,因此抽象出CompositeHealthIndicatorConfiguration,简化开发.

DiskSpaceHealthIndicator

  1. DiskSpaceHealthIndicator继承自AbstractHealthIndicator,声明了如下字段:
private final DiskSpaceHealthIndicatorProperties properties;

DiskSpaceHealthIndicatorProperties该类的作用是通过外置的方式对DiskSpaceHealthIndicator进行设置,其代码如下:

@ConfigurationProperties(prefix = "management.health.diskspace")
public class DiskSpaceHealthIndicatorProperties {

// 常量值
private static final int MEGABYTES = 1024 * 1024;

// 阈值为10M
private static final int DEFAULT_THRESHOLD = 10 * MEGABYTES;

// 用来计算可用空间的路径,默认为当前路径
private File path = new File(".");

// 最小可用的磁盘空间,单位为字节,默认为10M
private long threshold = DEFAULT_THRESHOLD;

// 省略get,set...
}

由于其声明了@ConfigurationProperties(prefix = “management.health.diskspace”)注解,因此可以通过如下进行配置:

management.health.diskspace.enabled=true # Enable disk space health check.
management.health.diskspace.path= # Path used to compute the available disk space.
management.health.diskspace.threshold=0 # Minimum disk space that should be available, in bytes.
  1. doHealthCheck 实现如下:
protected void doHealthCheck(Health.Builder builder) throws Exception {
    // 1. 获得配置的路径,如果当前路径的可用空间大于配置的阈值,则状态为up,否则为down
    File path = this.properties.getPath();
    long diskFreeInBytes = path.getUsableSpace();
    if (diskFreeInBytes >= this.properties.getThreshold()) {
        builder.up();
    }
    else {

        builder.down();
    }
    // 2. 设置属性值: total--> 配置检查路径的总空间, free--> 可用空间,threshold--> 配置的阈值,默认为10M
    builder.withDetail("total", path.getTotalSpace())
            .withDetail("free", diskFreeInBytes)
            .withDetail("threshold", this.properties.getThreshold());
}
  1. 获得配置的路径,如果当前路径的可用空间大于配置的阈值,则状态为up,否则为down
  2. 设置属性值: total–> 配置检查路径的总空间, free–> 可用空间,threshold–> 配置的阈值,默认为10M
  1. 自动装配:
  1. 在DiskSpaceHealthIndicatorConfiguration中进行了声明,该类声明了如下注解:
@Configuration
@ConditionalOnEnabledHealthIndicator("diskspace")
  • @Configuration –> 配置类
  • @ConditionalOnEnabledHealthIndicator(“diskspace”)–> 如果配置有management.health.diskspace.enabled = true 或者management.health.defaults.enabled = true 则该配置生效.
  1. @Bean方法声明如下:
  1. diskSpaceHealthIndicatorProperties,向beanFactory 注册了1个id为diskSpaceHealthIndicatorProperties,类型为DiskSpaceHealthIndicatorProperties的bean。代码如下:
@Bean
public DiskSpaceHealthIndicatorProperties diskSpaceHealthIndicatorProperties() {
return new DiskSpaceHealthIndicatorProperties();
}
  1. diskSpaceHealthIndicator,代码如下:
@Bean
@ConditionalOnMissingBean(name = "diskSpaceHealthIndicator")
public DiskSpaceHealthIndicator diskSpaceHealthIndicator(
    DiskSpaceHealthIndicatorProperties properties) {
return new DiskSpaceHealthIndicator(properties);
}
  • @Bean –> 注册1个id为diskSpaceHealthIndicator,类型为HealthIndicator的bean
  • @ConditionalOnMissingBean(name = “diskSpaceHealthIndicator”) –> 当beanFactory中不存在id为diskSpaceHealthIndicator 的bean 时生效.

CompositeHealthIndicator

  1. 该类直接实现了HealthIndicator,内部持有了Map 容器–> 持有了一系列的HealthIndicator的实例,是组合模式的范例.其构造器如下:
public CompositeHealthIndicator(HealthAggregator healthAggregator,
        Map<String, HealthIndicator> indicators) {
    Assert.notNull(healthAggregator, "HealthAggregator must not be null");
    Assert.notNull(indicators, "Indicators must not be null");
    this.indicators = new LinkedHashMap<String, HealthIndicator>(indicators);
    this.healthAggregator = healthAggregator;
}
  1. CompositeHealthIndicator 声明了添加HealthIndicator的方法,代码如下:
public void addHealthIndicator(String name, HealthIndicator indicator) {
    this.indicators.put(name, indicator);
}
  1. 此外,其内部还持有了HealthAggregator的实例,HealthAggregator–>策略接口 通过CompositeHealthIndicator来聚合 Health的实例为一个.代码如下:
public interface HealthAggregator {

// 聚合一系列的Health为一个
Health aggregate(Map<String, Health> healths);

}

其继承结构如下:

springboot 健康检查地址 springboot health_实例化_04

  1. AbstractHealthAggregator 实现了aggregate,代码如下:
public final Health aggregate(Map<String, Health> healths) {
List<Status> statusCandidates = new ArrayList<Status>();
// 1. 遍历healths,依次添加Health至statusCandidates中
for (Map.Entry<String, Health> entry : healths.entrySet()) {
    statusCandidates.add(entry.getValue().getStatus());
}
// 2. 返回一个聚合后的状态-->通过使用传入的candidates
Status status = aggregateStatus(statusCandidates);
// 3. 生成一个聚合后的details,在这里的实现就是简单的将传入的healths 添加至LinkedHashMap中
Map<String, Object> details = aggregateDetails(healths);
// 4. 通过Health.Builder 根据生成的status,details 构建Health
return new Health.Builder(status, details).build();
}
  1. 遍历healths,依次添加Health至statusCandidates中
  2. 返回一个聚合后的状态–>通过使用传入的candidates,抽象方法,子类实现
  3. 生成一个聚合后的details,在这里的实现就是简单的将传入的healths 添加至LinkedHashMap中.代码如下:
protected Map<String, Object> aggregateDetails(Map<String, Health> healths) {
    return new LinkedHashMap<String, Object>(healths);
}
  1. 通过Health.Builder 根据生成的status,details 构建Health
  1. OrderedHealthAggregator–>继承自AbstractHealthAggregator,其内部定义了1个名为statusOrder的List,用来存放对什么状态的数据进行聚合.默认为Status.DOWN, Status.OUT_OF_SERVICE, Status.UP, Status.UNKNOWN.代码如下:
public OrderedHealthAggregator() {
setStatusOrder(Status.DOWN, Status.OUT_OF_SERVICE, Status.UP, Status.UNKNOWN);
}

其aggregateStatus 代码如下:

  1. 依次遍历candidates,默认情况下只将状态为Status.DOWN, Status.OUT_OF_SERVICE, Status.UP, Status.UNKNOWN 的添加至filteredCandidates中.
  2. 如果filteredCandidates为空,则返回UNKNOWN
  3. 排序后,返回第一个的状态,此时使用的是StatusComparator,其比较逻辑如下:
    比较给定的2个Status 在statusOrder中的下标,如果下标相同,则比较其 code值的大小.statusOrder中的顺序为: Status.DOWN, Status.OUT_OF_SERVICE, Status.UP, Status.UNKNOWN
    代码如下:
public int compare(Status s1, Status s2) {
int i1 = this.statusOrder.indexOf(s1.getCode());
int i2 = this.statusOrder.indexOf(s2.getCode());
return (i1 < i2 ? -1 : (i1 == i2 ? s1.getCode().compareTo(s2.getCode()) : 1));
}
  1. 视线回到CompositeHealthIndicator中,其health的实现就比较简单了,通过遍历其indicators,依次添加至healths中,最后调用HealthAggregator#aggregate 进行聚合.代码如下:
public Health health() {
    // 遍历indicators,依次添加至healths中,最后调用HealthAggregator#aggregate 进行聚合.
    Map<String, Health> healths = new LinkedHashMap<String, Health>();
    for (Map.Entry<String, HealthIndicator> entry : this.indicators.entrySet()) {
        healths.put(entry.getKey(), entry.getValue().health());
    }
    return this.healthAggregator.aggregate(healths);
}
  1. 自动装配–>无

HealthEndpoint 解析

回到本文的重头戏–> HealthEndpoint.

  1. 作用–> 通过CompositeHealthIndicator来聚合spring boot应用中装配的HealthIndicator,在invoke中,依次展示其详情
  2. 声明的字段如下:
private final HealthIndicator healthIndicator;

// 缓存失效时间
private long timeToLive = 1000;

构造器如下:

public HealthEndpoint(HealthAggregator healthAggregator,
        Map<String, HealthIndicator> healthIndicators) {
    super("health", false);
    Assert.notNull(healthAggregator, "HealthAggregator must not be null");
    Assert.notNull(healthIndicators, "HealthIndicators must not be null");
    // 1. 实例化CompositeHealthIndicator
    CompositeHealthIndicator healthIndicator = new CompositeHealthIndicator(
            healthAggregator);
    // 2. 遍历healthIndicators,依次进行添加
    for (Map.Entry<String, HealthIndicator> entry : healthIndicators.entrySet()) {
        healthIndicator.addHealthIndicator(getKey(entry.getKey()), entry.getValue());
    }
    // 3. 赋值给healthIndicator
    this.healthIndicator = healthIndicator;
}
  1. 实例化CompositeHealthIndicator
  2. 遍历healthIndicators,依次进行添加
  3. 赋值给healthIndicator
  1. invoke,只需调用CompositeHealthIndicator#health 即可.代码如下:
public Health invoke() {
    return this.healthIndicator.health();
}
  1. 属性配置–> 因为HealthEndpoint声明了@ConfigurationProperties(prefix = “endpoints.health”) 注解,因此可以如下进行配置:
endpoints.health.enabled= # Enable the endpoint.
endpoints.health.id= # Endpoint identifier.
endpoints.health.sensitive= # Mark if the endpoint exposes sensitive information.
endpoints.health.time-to-live=1000 # Time to live for cached result, in milliseconds.
  1. 自动装配–>声明在EndpointAutoConfiguration类中,代码如下:
@Bean
@ConditionalOnMissingBean
public HealthEndpoint healthEndpoint() {
    return new HealthEndpoint(
            this.healthAggregator == null ? new OrderedHealthAggregator()
                    : this.healthAggregator,
            this.healthIndicators == null
                    ? Collections.<String, HealthIndicator>emptyMap()
                    : this.healthIndicators);
}
  • @Bean –> 注册1个id为healthEndpoint,类型为HealthEndpoint的bean
  • @ConditionalOnMissingBean –> 当beanFactory中不存在HealthEndpoint类型的bean 时生效.
  • 默认使用的OrderedHealthAggregator