spring boot 源码解析24-HealthEndpoint解析

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】
免费领取10G资料包与项目实战视频资料

前言

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);
              }
          }
          }
      
      
      1. 获得数据库产商,并添加至builder中,代码如下:

                private String getProduct() {
            return this.jdbcTemplate.execute(new ConnectionCallback() {
            @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;
            }
    3. 自动装配:

      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
    4. LdapHealthIndicator

      1. 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;
                }
        
            }
      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() {
                    @Override
                    public String doInRabbit(Channel channel) throws Exception {
                        Map 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 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;
            }
      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 healths);
        
            }

        其继承结构如下:

        20191017100461\_4.png

        1. 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();
              }
          1. 遍历healths,依次添加Health至statusCandidates中
          2. 返回一个聚合后的状态–>通过使用传入的candidates,抽象方法,子类实现
          3. 生成一个聚合后的details,在这里的实现就是简单的将传入的healths 添加至LinkedHashMap中.代码如下:

                protected Map aggregateDetails(Map healths) {
                    return new LinkedHashMap(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 healths = new LinkedHashMap();
                for (Map.Entry 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 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;
            }
        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.emptyMap()
                                : this.healthIndicators);
            }
        • @Bean –> 注册1个id为healthEndpoint,类型为HealthEndpoint的bean
        • @ConditionalOnMissingBean –> 当beanFactory中不存在HealthEndpoint类型的bean 时生效.
        • 默认使用的OrderedHealthAggregator

      来源:[]()

      赞(0) 打赏
      版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » spring boot 源码解析24-HealthEndpoint解析

      评论 抢沙发

      • 昵称 (必填)
      • 邮箱 (必填)
      • 网址

      © 2014 - 2020 Java 技术驿站   网站地图  | 

      icp 湘ICP备14000180

      >>> 网站已平稳运行:

      觉得文章有用就打赏一下文章作者

      支付宝扫一扫打赏

      微信扫一扫打赏