spring boot 源码解析24-HealthEndpoint解析

前言

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

health包结构图

整理成类图如下:

HealthIndicator类图

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

解析

HealthIndicator

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

public interface HealthIndicator {

    // 返回health
    Health health();

}

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

Health-class-uml

  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");
  2. Health 类同样也声明了@JsonInclude(Include.NON_EMPTY) 注解,表明如果该类中所有属性如果为空(“”) 或者为NULL则不序列化.

    声明了如下字段:

    // 状态
    private final Status status;
    
    // 详情
    private final Map<String, Object> details;
  3. Builder 则是定义在 Health类中,则定义的字段和Health中的一样.则构造器如下:

    public Builder() {
            this.status = Status.UNKNOWN;
            this.details = new LinkedHashMap<String, Object>();
    }

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

  4. 如何使用 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();
        }
  2. 自动化配置:

    在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;
    }
  2. 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.
  3. 自动化装配:

    定义在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中
    2. 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类型的构造器进行实例化.
    3. 由于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) 是不会生效的.

    4. 同时,在@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;
      }
      

      然后进行实例化.

    2. 当前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;
    }
  2. 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–> 版本号,如果有多个的话,会通过,进行拼接
  3. 自动装配:

    同样和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 则该配置生效.
    2. 其@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;
  2. DataSourceHealthIndicator 实现了InitializingBean接口,因此在初始化后会调用afterPropertiesSet方法,代码如下:

    public void afterPropertiesSet() throws Exception {
        Assert.state(this.dataSource != null,
                "DataSource for DataSourceHealthIndicator must be specified");
    }
  3. 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();
        }
        });
        }
      2. 获得测试的语句,默认为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;
        }
  4. 自动装配:

    1. 定义在DataSourcesHealthIndicatorConfiguration中,该类继承了CompositeHealthIndicatorConfiguration,泛型参数为DataSourceHealthIndicator, DataSource,同时实现了InitializingBean 接口.故会在初始化调用其afterPropertiesSet方法,代码如下:

      public void afterPropertiesSet() throws Exception {
          this.poolMetadataProvider = new DataSourcePoolMetadataProviders(
                  this.metadataProviders);
      }

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

    2. 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 则该配置生效.
    3. 在@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 时生效.
    4. 由于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;
        }
    
    }
  2. 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)
  3. 自动装配:

    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 则该配置生效.
    3. @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;
    }
  2. 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"));
    }
  3. 自动装配:

    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 则该配置生效.
    3. @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;
    }
  2. 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. 释放链接
  3. 自动装配:

    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 则该配置生效.
    3. @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;
    }
  2. 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();
            }
        });
    }
    
  3. 自动装配:

    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 则该配置生效.
    3. @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;
    }
  2. 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));
    }
  3. 自动装配:

    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 则该配置生效.
    3. @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;
    }
  2. 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
  3. 自动装配:

    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 则该配置生效.
    3. @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;
    }
  2. 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();
        }
    }
  3. 自动装配:

    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 则该配置生效.
    3. @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.
  2. 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

  3. 自动装配:

    1. 在DiskSpaceHealthIndicatorConfiguration中进行了声明,该类声明了如下注解:

      @Configuration
      @ConditionalOnEnabledHealthIndicator("diskspace")
      • @Configuration –> 配置类
      • @ConditionalOnEnabledHealthIndicator(“diskspace”)–> 如果配置有management.health.diskspace.enabled = true 或者management.health.defaults.enabled = true 则该配置生效.
    2. @Bean方法声明如下:

      1. diskSpaceHealthIndicatorProperties,向beanFactory 注册了1个id为diskSpaceHealthIndicatorProperties,类型为DiskSpaceHealthIndicatorProperties的bean。代码如下:

        @Bean
        public DiskSpaceHealthIndicatorProperties diskSpaceHealthIndicatorProperties() {
        return new DiskSpaceHealthIndicatorProperties();
        }
      2. 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;
    }
  2. CompositeHealthIndicator 声明了添加HealthIndicator的方法,代码如下:

    public void addHealthIndicator(String name, HealthIndicator indicator) {
        this.indicators.put(name, indicator);
    }
  3. 此外,其内部还持有了HealthAggregator的实例,HealthAggregator–>策略接口 通过CompositeHealthIndicator来聚合 Health的实例为一个.代码如下:

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

    其继承结构如下:

    HealthAggregator继承结构

    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);
        }
      4. 通过Health.Builder 根据生成的status,details 构建Health
    2. 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));
        }
  4. 视线回到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);
    }
  5. 自动装配–>无

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
  3. invoke,只需调用CompositeHealthIndicator#health 即可.代码如下:

    public Health invoke() {
        return this.healthIndicator.health();
    }
  4. 属性配置–> 因为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.
  5. 自动装配–>声明在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

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值