spring boot 源码解析24-HealthEndpoint解析

 2019-10-17 22:07  阅读(1665)
文章分类:Spring boot

前言

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

20191017100461\_1.png

整理成类图如下:

20191017100461\_2.png

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

解析

HealthIndicator

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

public interface HealthIndicator {

        // 返回health
        Health health();

    }

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

20191017100461\_3.png

  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 details;
    
  3. Builder 则是定义在 Health类中,则定义的字段和Health中的一样.则构造器如下:

    public Builder() {
                this.status = Status.UNKNOWN;
                this.details = new LinkedHashMap();
        }
    

    意味着:默认情况下,构建出来的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 代表 HealthIndicator 的类型,泛型参数S代表bean的原始类型(the bean source type).

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

    1. createHealthIndicator,代码如下:

      protected HealthIndicator createHealthIndicator(Map beans) {
          if (beans.size() == 1) {
              return createHealthIndicator(beans.values().iterator().next());
          }
          CompositeHealthIndicator composite = new CompositeHealthIndicator(
                  this.healthAggregator);
          for (Map.Entry 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 indicatorClass = (Class) generics[0];
          Class sourceClass = (Class) 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
    

    因此,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 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
    
    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 results = this.jdbcTemplate.query(validationQuery,
                          new SingleColumnRowMapper());
                  Object result = DataAccessUtils.requiredSingleResult(results);
                  builder.withDetail("hello", result);
              }
              catch (Exception ex) {
                  builder.down(ex);
              }
          }
          }
      
       获得数据库产商,并添加至builder中,代码如下:         private String getProduct() {
          return this.jdbcTemplate.execute(new ConnectionCallback() {
          @Override
          public String doInConnection(Connection connection)
                  throws SQLException, DataAccessException {
              return connection.getMetaData().getDatabaseProductName();
          }
          });
          } 获得测试的语句,默认为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;
          } 
      
      
      
       自动装配: 定义在DataSourcesHealthIndicatorConfiguration中,该类继承了CompositeHealthIndicatorConfiguration,泛型参数为DataSourceHealthIndicator, DataSource,同时实现了InitializingBean 接口.故会在初始化调用其afterPropertiesSet方法,代码如下:     public void afterPropertiesSet() throws Exception {
              this.poolMetadataProvider = new DataSourcePoolMetadataProviders(
                      this.metadataProviders);
          } 实例化了 DataSourcePoolMetadataProviders,该类实现了DataSourcePoolMetadataProvider,封装了数据库的元数据.关于这部分的代码我们在下篇文章进行分析 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 则该配置生效. 在@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 时生效. 由于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());
          } 如果DataSourcePoolMetadata 存在,则获得DataSourcePoolMetadata中配置的检查sql,此时分2种情况,如果配置了sql,则进行检查时,执行的是配置的sql语句,否则,使用的是默认的sql–> Select 1 否则,返回null,此时使用的是默认的sql–>Select 1 
      
       LdapHealthIndicator
       LdapHealthIndicator定义了如下字段:     private static final ContextExecutor versionContextExecutor = new VersionContextExecutor();
      
          private final LdapOperations ldapOperations; 其中VersionContextExecutor 会在doHealthCheck 中用到,其实现了org.springframework.ldap.core.ContextExecutor 接口,代码如下:         private static class VersionContextExecutor implements ContextExecutor {
      
              @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;
              }
      
          } 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);
          } 通过LdapOperations 来获取java.naming.ldap.version的值.如果获取不到,则返回null 如果版本号不等于null,则设置为up,属性值 version –> 版本号,否则,设置为down(在withDetail中对value进行了断言,此时传入null,导致断言失败,抛出异常,因此会设置为down) 自动装配: 在LdapHealthIndicatorConfiguration中声明,该类继承了CompositeHealthIndicatorConfiguration,泛型参数分别为LdapHealthIndicator, LdapOperations 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 则该配置生效. @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
       MongoHealthIndicator 继承了AbstractHealthIndicator,有如下字段:     private final MongoTemplate mongoTemplate; 构造器为:     public MongoHealthIndicator(MongoTemplate mongoTemplate) {
              Assert.notNull(mongoTemplate, "MongoTemplate must not be null");
              this.mongoTemplate = mongoTemplate;
          } 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"));
          } 自动装配: 在MongoHealthIndicatorConfiguration中定义,继承了CompositeHealthIndicatorConfiguration,泛型参数为MongoHealthIndicator, MongoTemplate 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 则该配置生效. @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
       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;
          } 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);
              }
          }
       获得redis链接 如果是集群的话,则获得集群的信息.并设置状态为up,属性值cluster_size–>集群节点数量,slots_up –> 可用槽的数量, slots_fail –> 不可用槽的数量 如果不是集群的话,则直接获取版本号 释放链接 自动装配: 在RedisHealthIndicatorConfiguration中定义,继承了CompositeHealthIndicatorConfiguration,泛型参数分别为RedisHealthIndicator, RedisConnectionFactory 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 则该配置生效. @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
       RabbitHealthIndicator 继承AbstractHealthIndicator,声明了如下字段:     private final RabbitTemplate rabbitTemplate; 构造器如下:     public RabbitHealthIndicator(RabbitTemplate rabbitTemplate) {
              Assert.notNull(rabbitTemplate, "RabbitTemplate must not be null.");
              this.rabbitTemplate = rabbitTemplate;
          } 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() {
                  @Override
                  public String doInRabbit(Channel channel) throws Exception {
                      Map serverProperties = channel.getConnection()
                              .getServerProperties();
                      return serverProperties.get("version").toString();
                  }
              });
          }
       自动装配: 在RabbitHealthIndicatorConfiguration中,继承了CompositeHealthIndicatorConfiguration,泛型参数分别为RabbitHealthIndicator, RabbitTemplate. 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 则该配置生效. @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
       SolrHealthIndicator 继承自AbstractHealthIndicator,声明字段如下:     private final SolrClient solrClient; 构造器如下:     public SolrHealthIndicator(SolrClient solrClient) {
              this.solrClient = solrClient;
          } 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));
          } 自动装配: 在SolrHealthIndicatorConfiguration中,继承自CompositeHealthIndicatorConfiguration,泛型参数分别为SolrHealthIndicator, SolrClient 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 则该配置生效. @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
       MailHealthIndicator 继承自AbstractHealthIndicator,其内部持有JavaMailSenderImpl的实例,构造器如下:     public MailHealthIndicator(JavaMailSenderImpl mailSender) {
              this.mailSender = mailSender;
          } 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();
          } 设置属性location–>mailSender配置的主机名:mailSender配置的端口号 测试链接,如果成功,则状态为up,否则为down 自动装配: 声明在MailHealthIndicatorConfiguration,该类继承自CompositeHealthIndicatorConfiguration,泛型参数为MailHealthIndicator, JavaMailSenderImpl 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 则该配置生效. @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
       JmsHealthIndicator 继承自AbstractHealthIndicator,其内部持有ConnectionFactory,通过构造器注入,代码如下:     public JmsHealthIndicator(ConnectionFactory connectionFactory) {
              this.connectionFactory = connectionFactory;
          } 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();
              }
          } 自动装配: 声明在JmsHealthIndicatorConfiguration中,继承自CompositeHealthIndicatorConfiguration,泛型参数分别为JmsHealthIndicator, ConnectionFactory 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 则该配置生效. @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个则直接实例化对应的HealthIndicator即可 否则,就实例化CompositeHealthIndicator,对配置的资源的健康情况做监控,最后给出一个聚合的结果(后面有详解) 
       同样,资源有很多,我们如果每个资源都写一套逻辑的话,就太Low了,因此抽象出CompositeHealthIndicatorConfiguration,简化开发.
       DiskSpaceHealthIndicator
       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. 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());
          } 获得配置的路径,如果当前路径的可用空间大于配置的阈值,则状态为up,否则为down 设置属性值: total–> 配置检查路径的总空间, free–> 可用空间,threshold–> 配置的阈值,默认为10M 自动装配: 在DiskSpaceHealthIndicatorConfiguration中进行了声明,该类声明了如下注解:     @Configuration
          @ConditionalOnEnabledHealthIndicator("diskspace") @Configuration –> 配置类 @ConditionalOnEnabledHealthIndicator(“diskspace”)–> 如果配置有management.health.diskspace.enabled = true 或者management.health.defaults.enabled = true 则该配置生效. @Bean方法声明如下: diskSpaceHealthIndicatorProperties,向beanFactory 注册了1个id为diskSpaceHealthIndicatorProperties,类型为DiskSpaceHealthIndicatorProperties的bean。代码如下:     @Bean
          public DiskSpaceHealthIndicatorProperties diskSpaceHealthIndicatorProperties() {
          return new DiskSpaceHealthIndicatorProperties();
          } 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
       该类直接实现了HealthIndicator,内部持有了Map 容器–> 持有了一系列的HealthIndicator的实例,是组合模式的范例.其构造器如下:     public CompositeHealthIndicator(HealthAggregator healthAggregator,
                  Map indicators) {
              Assert.notNull(healthAggregator, "HealthAggregator must not be null");
              Assert.notNull(indicators, "Indicators must not be null");
              this.indicators = new LinkedHashMap(indicators);
              this.healthAggregator = healthAggregator;
          } CompositeHealthIndicator 声明了添加HealthIndicator的方法,代码如下:     public void addHealthIndicator(String name, HealthIndicator indicator) {
              this.indicators.put(name, indicator);
          } 此外,其内部还持有了HealthAggregator的实例,HealthAggregator–>策略接口 通过CompositeHealthIndicator来聚合 Health的实例为一个.代码如下:     public interface HealthAggregator {
      
          // 聚合一系列的Health为一个
          Health aggregate(Map healths);
      
          } 其继承结构如下: AbstractHealthAggregator 实现了aggregate,代码如下:     public final Health aggregate(Map healths) {
          List statusCandidates = new ArrayList();
          // 1. 遍历healths,依次添加Health至statusCandidates中
          for (Map.Entry entry : healths.entrySet()) {
              statusCandidates.add(entry.getValue().getStatus());
          }
          // 2. 返回一个聚合后的状态-->通过使用传入的candidates
          Status status = aggregateStatus(statusCandidates);
          // 3. 生成一个聚合后的details,在这里的实现就是简单的将传入的healths 添加至LinkedHashMap中
          Map details = aggregateDetails(healths);
          // 4. 通过Health.Builder 根据生成的status,details 构建Health
          return new Health.Builder(status, details).build();
          } 遍历healths,依次添加Health至statusCandidates中 返回一个聚合后的状态–>通过使用传入的candidates,抽象方法,子类实现 生成一个聚合后的details,在这里的实现就是简单的将传入的healths 添加至LinkedHashMap中.代码如下:     protected Map aggregateDetails(Map healths) {
              return new LinkedHashMap(healths);
          } 通过Health.Builder 根据生成的status,details 构建Health 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 代码如下: 依次遍历candidates,默认情况下只将状态为Status.DOWN, Status.OUT_OF_SERVICE, Status.UP, Status.UNKNOWN 的添加至filteredCandidates中. 如果filteredCandidates为空,则返回UNKNOWN 排序后,返回第一个的状态,此时使用的是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));
          } 视线回到CompositeHealthIndicator中,其health的实现就比较简单了,通过遍历其indicators,依次添加至healths中,最后调用HealthAggregator#aggregate 进行聚合.代码如下:     public Health health() {
              // 遍历indicators,依次添加至healths中,最后调用HealthAggregator#aggregate 进行聚合.
              Map healths = new LinkedHashMap();
              for (Map.Entry entry : this.indicators.entrySet()) {
                  healths.put(entry.getKey(), entry.getValue().health());
              }
              return this.healthAggregator.aggregate(healths);
          } 自动装配–>无 
       HealthEndpoint 解析
       回到本文的重头戏–> HealthEndpoint.
       作用–> 通过CompositeHealthIndicator来聚合spring boot应用中装配的HealthIndicator,在invoke中,依次展示其详情 声明的字段如下:     private final HealthIndicator healthIndicator;
      
          // 缓存失效时间
          private long timeToLive = 1000;
       构造器如下:     public HealthEndpoint(HealthAggregator healthAggregator,
                  Map 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 entry : healthIndicators.entrySet()) {
                  healthIndicator.addHealthIndicator(getKey(entry.getKey()), entry.getValue());
              }
              // 3. 赋值给healthIndicator
              this.healthIndicator = healthIndicator;
          } 实例化CompositeHealthIndicator 遍历healthIndicators,依次进行添加 赋值给healthIndicator invoke,只需调用CompositeHealthIndicator#health 即可.代码如下:     public Health invoke() {
              return this.healthIndicator.health();
          } 属性配置–> 因为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. 自动装配–>声明在EndpointAutoConfiguration类中,代码如下:     @Bean
          @ConditionalOnMissingBean
          public HealthEndpoint healthEndpoint() {
              return new HealthEndpoint(
                      this.healthAggregator == null ? new OrderedHealthAggregator()
                              : this.healthAggregator,
                      this.healthIndicators == null
                              ? Collections.emptyMap()
                              : this.healthIndicators);
          } @Bean –> 注册1个id为healthEndpoint,类型为HealthEndpoint的bean @ConditionalOnMissingBean –> 当beanFactory中不存在HealthEndpoint类型的bean 时生效. 默认使用的OrderedHealthAggregator 
      
       来源:[]()
      
点赞(0)
版权归原创作者所有,任何形式转载请联系作者; Java 技术驿站 >> spring boot 源码解析24-HealthEndpoint解析

相关推荐