spring boot 源码解析24-HealthEndpoint解析

网友投稿 248 2022-10-09

spring boot 源码解析24-HealthEndpoint解析

spring boot 源码解析24-HealthEndpoint解析

一个努力的码农 2018-01-15 15:26:38 16686 收藏 1

分类专栏: spring boot spring boot 源码解析 文章标签: spring 源码 class

版权

spring boot

同时被 2 个专栏收录

57 篇文章10 订阅

订阅专栏

spring boot 源码解析

58 篇文章106 订阅

订阅专栏

前言

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

整理成类图如下:

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

解析

HealthIndicator

public interface HealthIndicator {

// 返回health

Health health();

}

1

2

3

4

5

6

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

@JsonInclude(Include.NON_EMPTY)

1

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

其内部定义了如下字段:

// 状态码

private final String code;

// 描述

private final String description;

1

2

3

4

5

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

// 系统状态未知

public static final Status UNKNOWN = new Status("UNKNOWN");

// 可用

public static final Status UP = new Status("UP");

// 服务挂掉

public static final Status DOWN = new Status("DOWN");

// 服务不可用

public static final Status OUT_OF_SERVICE = new Status("OUT_OF_SERVICE");

1

2

3

4

5

6

7

8

9

10

11

// 状态

private final Status status;

// 详情

private final Map details;

1

2

3

4

5

Builder 则是定义在 Health类中,则定义的字段和Health中的一样.则构造器如下:

public Builder() {

this.status = Status.UNKNOWN;

this.details = new LinkedHashMap();

}

1

2

3

4

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

如何使用 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();

}

1

2

3

4

5

6

7

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

AbstractHealthIndicator

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

2

3

4

5

6

7

8

9

10

11

12

13

14

15

实例化Health$Builder

调用抽象方法doHealthCheck–>进行状态的检查,如果在检查过程中出现异常,则状态为Status.DOWN

构建Health

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

ApplicationHealthIndicator

该类的实现比较简单,通过继承AbstractHealthIndicator,实现其doHealthCheck,在该方法中直接将其设置为up.代码如下:

protected void doHealthCheck(Health.Builder builder) throws Exception {

builder.up();

}

1

2

3

自动化配置:

在HealthIndicatorAutoConfiguration 进行了配置,代码如下:

@Bean

@ConditionalOnMissingBean(HealthIndicator.class)

public ApplicationHealthIndicator applicationHealthIndicator() {

return new ApplicationHealthIndicator();

}

1

2

3

4

5

@Bean –> 注册1个id为applicationHealthIndicator,类型为ApplicationHealthIndicator的bean

@ConditionalOnMissingBean(HealthIndicator.class) –> 当beanFactory中不存在HealthIndicator类型的bean时生效

CassandraHealthIndicator

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

private CassandraOperations cassandraOperations;

public CassandraHealthIndicator(CassandraOperations cassandraOperations) {

Assert.notNull(cassandraOperations, "CassandraOperations must not be null");

this.cassandraOperations = cassandraOperations;

}

1

2

3

4

5

6

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

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

通过CassandraOperations 查询 在system(keyspace)下的local表中的 release_version 字段

如果执行成功,但是没有返回值,则设置为up,然后return

否则,设置状态为up,并添加key–>version,value–> 结果值到详情中

如果在查询过程中出现异常,则设置为状态为down.

自动化装配:

public abstract class CompositeHealthIndicatorConfiguration

1

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

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

2

3

4

5

6

7

8

9

10

11

12

如果传入的beans的size 为 1,则调用createHealthIndicator 创建HealthIndicator

否则,创建CompositeHealthIndicator,遍历传入的beans,依次创建HealthIndicator,加入到CompositeHealthIndicator中

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

2

3

4

5

6

7

8

9

10

11

12

13

14

获得CompositeHealthIndicatorConfiguration中的泛型参数

@Configuration

@ConditionalOnClass({ CassandraOperations.class, Cluster.class })

@ConditionalOnBean(CassandraOperations.class)

@ConditionalOnEnabledHealthIndicator("cassandra")

1

2

3

4

@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) 是不会生效的.

@Bean

@ConditionalOnMissingBean(name = "cassandraHealthIndicator")

public HealthIndicator cassandraHealthIndicator() {

return createHealthIndicator(this.cassandraOperations);

}

1

2

3

4

5

@Bean –> 注册1个id为cassandraHealthIndicator,类型为HealthIndicator的bean

@ConditionalOnMissingBean(name = “cassandraHealthIndicator”) –> 当beanFactory中不存在id为cassandraHealthIndicator 的bean 时生效.

public static class CassandraHealthIndicatorConfiguration extends

CompositeHealthIndicatorConfiguration

1

2

因此,H为 CassandraHealthIndicator,S 为 CassandraOperations.

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

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

当前beanFactory中只存在1个CassandraOperations的实例:

public CassandraHealthIndicator(CassandraOperations cassandraOperations) {

Assert.notNull(cassandraOperations, "CassandraOperations must not be null");

this.cassandraOperations = cassandraOperations;

}

1

2

3

4

5

然后进行实例化.

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

CouchbaseHealthIndicator

CouchbaseHealthIndicator和CassandraHealthIndicator类似,其内部持有CassandraOperations,构造器如下:

private CassandraOperations cassandraOperations;

public CassandraHealthIndicator(CassandraOperations cassandraOperations) {

Assert.notNull(cassandraOperations, "CassandraOperations must not be null");

this.cassandraOperations = cassandraOperations;

}

1

2

3

4

5

6

7

doHealthCheck 实现如下:

protected void doHealthCheck(Health.Builder builder) throws Exception {

List versions = this.couchbaseOperations.getCouchbaseClusterInfo()

.getAllVersions();

builder.up().withDetail("versions",

StringUtils.collectionToCommaDelimitedString(versions));

}

1

2

3

4

5

6

通过couchbaseOperations 获得版本号

如果访问成功的话,则设置状态为up,并设置key–> versions,value–> 版本号,如果有多个的话,会通过,进行拼接

自动装配:

同样和CassandraHealthIndicatorConfiguration 一样,继承自CompositeHealthIndicatorConfiguration,泛型参数分别为CouchbaseHealthIndicator, CouchbaseOperations,代码如下:

public static class CouchbaseHealthIndicatorConfiguration extends

CompositeHealthIndicatorConfiguration

1

2

@Configuration

@ConditionalOnClass({ CouchbaseOperations.class, Bucket.class })

@ConditionalOnBean(CouchbaseOperations.class)

@ConditionalOnEnabledHealthIndicator("couchbase")

1

2

3

4

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

其@Bean方法如下:

@Bean

@ConditionalOnMissingBean(name = "couchbaseHealthIndicator")

public HealthIndicator couchbaseHealthIndicator() {

return createHealthIndicator(this.couchbaseOperations);

}

1

2

3

4

5

@Bean –> 注册1个id为couchbaseHealthIndicator,类型为HealthIndicator的bean

@ConditionalOnMissingBean(name = “couchbaseHealthIndicator”) –> 当beanFactory中不存在id为couchbaseHealthIndicator 的bean 时生效.

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

DataSourceHealthIndicator

DataSourceHealthIndicator 中定义了如下字段:

// 默认的测试语句为SELECT 1

private static final String DEFAULT_QUERY = "SELECT 1";

// 数据库资源

private DataSource dataSource;

// 测试语句

private String query;

private JdbcTemplate jdbcTemplate;

1

2

3

4

5

6

7

8

9

10

DataSourceHealthIndicator 实现了InitializingBean接口,因此在初始化后会调用afterPropertiesSet方法,代码如下:

public void afterPropertiesSet() throws Exception {

Assert.state(this.dataSource != null,

"DataSource for DataSourceHealthIndicator must be specified");

}

1

2

3

4

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

2

3

4

5

6

7

8

9

10

11

如果DataSource没有配置,则直接返回up,message 为unknown

否则,调用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

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

获得数据库产商,并添加至builder中,代码如下:

private String getProduct() {

return this.jdbcTemplate.execute(new ConnectionCallback() {

@Override

public String doInConnection(Connection connection)

throws SQLException, DataAccessException {

return connection.getMetaData().getDatabaseProductName();

}

});

}

1

2

3

4

5

6

7

8

9

获得测试的语句,默认为SELECT 1,然后进行查询,将查询结果放入builder中,key–>hello,value–>结果值,如果出现异常,则调用Builder#down 设置状态为down.获得查询语句的代码如下:

protected String getValidationQuery(String product) {

// 1. 将配置的query 赋值为query

String query = this.query;

// 2. 如果没有配置的话,则通过DatabaseDriver获得

if (!StringUtils.hasText(query)) {

DatabaseDriver specific = DatabaseDriver.fromProductName(product);

query = specific.getValidationQuery();

}

// 3. 如果还没有配置的化,则返回默认的SELECT 1

if (!StringUtils.hasText(query)) {

query = DEFAULT_QUERY;

}

return query;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

自动装配:

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

public void afterPropertiesSet() throws Exception {

this.poolMetadataProvider = new DataSourcePoolMetadataProviders(

this.metadataProviders);

}

1

2

3

4

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

@Configuration

@ConditionalOnClass({ JdbcTemplate.class, AbstractRoutingDataSource.class })

@ConditionalOnBean(DataSource.class)

@ConditionalOnEnabledHealthIndicator("db")

1

2

3

4

@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

@ConditionalOnMissingBean(name = "dbHealthIndicator")

public HealthIndicator dbHealthIndicator() {

return createHealthIndicator(this.dataSources);

}

1

2

3

4

5

@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));

}

1

2

3

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

private String getValidationQuery(DataSource source) {

DataSourcePoolMetadata poolMetadata = this.poolMetadataProvider

.getDataSourcePoolMetadata(source);

return (poolMetadata == null ? null : poolMetadata.getValidationQuery());

}

1

2

3

4

5

如果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;

1

2

3

其中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;

}

}

1

2

3

4

5

6

7

8

9

10

11

12

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

2

3

4

5

6

通过LdapOperations 来获取java.naming.ldap.version的值.如果获取不到,则返回null

如果版本号不等于null,则设置为up,属性值 version –> 版本号,否则,设置为down(在withDetail中对value进行了断言,此时传入null,导致断言失败,抛出异常,因此会设置为down)

自动装配:

@Configuration

@ConditionalOnClass(LdapOperations.class)

@ConditionalOnBean(LdapOperations.class)

@ConditionalOnEnabledHealthIndicator("ldap")

1

2

3

4

@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

@ConditionalOnMissingBean(name = "ldapHealthIndicator")

public HealthIndicator ldapHealthIndicator() {

return createHealthIndicator(this.ldapOperations);

}

1

2

3

4

5

@Bean –> 注册1个id为ldapHealthIndicator,类型为HealthIndicator的bean

@ConditionalOnMissingBean(name = “ldapHealthIndicator”) –> 当beanFactory中不存在id为ldapHealthIndicator 的bean 时生效.

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

MongoHealthIndicator

MongoHealthIndicator 继承了AbstractHealthIndicator,有如下字段:

private final MongoTemplate mongoTemplate;

1

构造器为:

public MongoHealthIndicator(MongoTemplate mongoTemplate) {

Assert.notNull(mongoTemplate, "MongoTemplate must not be null");

this.mongoTemplate = mongoTemplate;

}

1

2

3

4

doHealthCheck 的实现很简单,通过MongoTemplate 执行{ buildInfo: 1 },如果成功的话,则设置为up并设置属性version–> 版本号,否则,设置为down.代码如下:

protected void doHealthCheck(Health.Builder builder) throws Exception {

CommandResult result = this.mongoTemplate.executeCommand("{ buildInfo: 1 }");

builder.up().withDetail("version", result.getString("version"));

}

1

2

3

4

自动装配:

在MongoHealthIndicatorConfiguration中定义,继承了CompositeHealthIndicatorConfiguration,泛型参数为MongoHealthIndicator, MongoTemplate

@Configuration

@ConditionalOnClass(MongoTemplate.class)

@ConditionalOnBean(MongoTemplate.class)

@ConditionalOnEnabledHealthIndicator("mongo")

1

2

3

4

@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);

}

1

2

3

4

5

@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;

1

2

3

4

5

构造器如下:

public RedisHealthIndicator(RedisConnectionFactory connectionFactory) {

Assert.notNull(connectionFactory, "ConnectionFactory must not be null");

this.redisConnectionFactory = connectionFactory;

}

1

2

3

4

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

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

获得redis链接

如果是集群的话,则获得集群的信息.并设置状态为up,属性值cluster_size–>集群节点数量,slots_up –> 可用槽的数量, slots_fail –> 不可用槽的数量

如果不是集群的话,则直接获取版本号

释放链接

自动装配:

在RedisHealthIndicatorConfiguration中定义,继承了CompositeHealthIndicatorConfiguration,泛型参数分别为RedisHealthIndicator, RedisConnectionFactory

@Configuration

@ConditionalOnClass(RedisConnectionFactory.class)

@ConditionalOnBean(RedisConnectionFactory.class)

@ConditionalOnEnabledHealthIndicator("redis")

1

2

3

4

@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);

}

1

2

3

4

5

@Bean –> 注册1个id为redisHealthIndicator,类型为HealthIndicator的bean

@ConditionalOnMissingBean(name = “redisHealthIndicator”) –> 当beanFactory中不存在id为redisHealthIndicator 的bean 时生效.

RabbitHealthIndicator

private final RabbitTemplate rabbitTemplate;

1

构造器如下:

public RabbitHealthIndicator(RabbitTemplate rabbitTemplate) {

Assert.notNull(rabbitTemplate, "RabbitTemplate must not be null.");

this.rabbitTemplate = rabbitTemplate;

}

1

2

3

4

doHealthCheck–>通过rabbitTemplate 查询版本号,如果查询成功的话,则设置状态为up,并设置属性值version–>RabbitMQ的版本号, 代码如下:

protected void doHealthCheck(Health.Builder builder) throws Exception {

builder.up().withDetail("version", getVersion());

}

1

2

3

其中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();

}

});

}

1

2

3

4

5

6

7

8

9

10

11

自动装配:

在RabbitHealthIndicatorConfiguration中,继承了CompositeHealthIndicatorConfiguration,泛型参数分别为RabbitHealthIndicator, RabbitTemplate.

@Configuration

@ConditionalOnClass(RabbitTemplate.class)

@ConditionalOnBean(RabbitTemplate.class)

@ConditionalOnEnabledHealthIndicator("rabbit")

1

2

3

4

@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);

}

1

2

3

4

5

@Bean –> 注册1个id为rabbitHealthIndicator,类型为HealthIndicator的bean

@ConditionalOnMissingBean(name = “rabbitHealthIndicator”) –> 当beanFactory中不存在id为rabbitHealthIndicator 的bean 时生效.

SolrHealthIndicator

private final SolrClient solrClient;

1

构造器如下:

public SolrHealthIndicator(SolrClient solrClient) {

this.solrClient = solrClient;

}

1

2

3

doHealthCheck,通过SolrClient 来获取solr的状态,如果返回的状态码为0,则设置为up,属性值为solrStatus–>OK,否则,设置为down,属性值为solrStatus–>返回的状态值,代码如下:

protected void doHealthCheck(Health.Builder builder) throws Exception {

CoreAdminRequest request = new CoreAdminRequest();

request.setAction(CoreAdminParams.CoreAdminAction.STATUS);

CoreAdminResponse response = request.process(this.solrClient);

int statusCode = response.getStatus();

Status status = (statusCode == 0 ? Status.UP : Status.DOWN);

builder.status(status).withDetail("solrStatus",

(statusCode == 0 ? "OK" : statusCode));

}

1

2

3

4

5

6

7

8

9

自动装配:

在SolrHealthIndicatorConfiguration中,继承自CompositeHealthIndicatorConfiguration,泛型参数分别为SolrHealthIndicator, SolrClient

@Configuration

@ConditionalOnClass(SolrClient.class)

@ConditionalOnBean(SolrClient.class)

@ConditionalOnEnabledHealthIndicator("solr")

1

2

3

4

@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);

}

1

2

3

4

5

@Bean –> 注册1个id为solrHealthIndicator,类型为HealthIndicator的bean

@ConditionalOnMissingBean(name = “solrHealthIndicator”) –> 当beanFactory中不存在id为solrHealthIndicator 的bean 时生效.

MailHealthIndicator

MailHealthIndicator 继承自AbstractHealthIndicator,其内部持有JavaMailSenderImpl的实例,构造器如下:

public MailHealthIndicator(JavaMailSenderImpl mailSender) {

this.mailSender = mailSender;

}

1

2

3

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

2

3

4

5

6

7

8

设置属性location–>mailSender配置的主机名:mailSender配置的端口号

测试链接,如果成功,则状态为up,否则为down

自动装配:

@Configuration

@ConditionalOnClass(JavaMailSenderImpl.class)

@ConditionalOnBean(JavaMailSenderImpl.class)

@ConditionalOnEnabledHealthIndicator("mail")

1

2

3

4

@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);

}

1

2

3

4

5

@Bean –> 注册1个id为mailHealthIndicator,类型为HealthIndicator的bean

@ConditionalOnMissingBean(name = “mailHealthIndicator”) –> 当beanFactory中不存在id为mailHealthIndicator 的bean 时生效.

JmsHealthIndicator

JmsHealthIndicator 继承自AbstractHealthIndicator,其内部持有ConnectionFactory,通过构造器注入,代码如下:

public JmsHealthIndicator(ConnectionFactory connectionFactory) {

this.connectionFactory = connectionFactory;

}

1

2

3

doHealthCheck–>检查方式为通过ConnectionFactory 来创建1个Connection,并调用其start 方法,如果正常的话,则设置为状态为up,否则设置为down(父类方法中的try-catch设置),检查完毕后,将Connection进行关闭(finally 块保证).代码如下:

protected void doHealthCheck(Health.Builder builder) throws Exception {

Connection connection = this.connectionFactory.createConnection();

try {

connection.start();

builder.up().withDetail("provider",

connection.getMetaData().getJMSProviderName());

}

finally {

connection.close();

}

}

1

2

3

4

5

6

7

8

9

10

11

自动装配:

@Configuration

@ConditionalOnClass(ConnectionFactory.class)

@ConditionalOnBean(ConnectionFactory.class)

@ConditionalOnEnabledHealthIndicator("jms")

1

2

3

4

@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);

}

1

2

3

4

5

@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

private final DiskSpaceHealthIndicatorProperties properties;

1

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

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

management.health.diskspace.enabled=true # Enable disk space health check.

management.health.diskspace.path= # Path used to compute the available disk space.

management.health.diskspace.threshold=0 # Minimum disk space that should be available, in bytes.

1

2

3

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

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

获得配置的路径,如果当前路径的可用空间大于配置的阈值,则状态为up,否则为down

设置属性值: total–> 配置检查路径的总空间, free–> 可用空间,threshold–> 配置的阈值,默认为10M

自动装配:

@Configuration

@ConditionalOnEnabledHealthIndicator("diskspace")

1

2

@Configuration –> 配置类

@ConditionalOnEnabledHealthIndicator(“diskspace”)–> 如果配置有management.health.diskspace.enabled = true 或者management.health.defaults.enabled = true 则该配置生效.

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

@Bean

public DiskSpaceHealthIndicatorProperties diskSpaceHealthIndicatorProperties() {

return new DiskSpaceHealthIndicatorProperties();

}

1

2

3

4

diskSpaceHealthIndicator,代码如下:

@Bean

@ConditionalOnMissingBean(name = "diskSpaceHealthIndicator")

public DiskSpaceHealthIndicator diskSpaceHealthIndicator(

DiskSpaceHealthIndicatorProperties properties) {

return new DiskSpaceHealthIndicator(properties);

}

1

2

3

4

5

6

@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;

}

1

2

3

4

5

6

7

public void addHealthIndicator(String name, HealthIndicator indicator) {

this.indicators.put(name, indicator);

}

1

2

3

此外,其内部还持有了HealthAggregator的实例,HealthAggregator–>策略接口 通过CompositeHealthIndicator来聚合 Health的实例为一个.代码如下:

public interface HealthAggregator {

// 聚合一系列的Health为一个

Health aggregate(Map healths);

}

1

2

3

4

5

6

其继承结构如下:

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

2

3

4

5

6

7

8

9

10

11

12

13

遍历healths,依次添加Health至statusCandidates中

返回一个聚合后的状态–>通过使用传入的candidates,抽象方法,子类实现

生成一个聚合后的details,在这里的实现就是简单的将传入的healths 添加至LinkedHashMap中.代码如下:

protected Map aggregateDetails(Map healths) {

return new LinkedHashMap(healths);

}

1

2

3

通过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);

}

1

2

3

其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));

}

1

2

3

4

5

视线回到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);

}

1

2

3

4

5

6

7

8

自动装配–>无

HealthEndpoint 解析

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

作用–> 通过CompositeHealthIndicator来聚合spring boot应用中装配的HealthIndicator,在invoke中,依次展示其详情

private final HealthIndicator healthIndicator;

// 缓存失效时间

private long timeToLive = 1000;

1

2

3

4

5

构造器如下:

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

2

3

4

5

6

7

8

9

10

11

12

13

14

15

实例化CompositeHealthIndicator

遍历healthIndicators,依次进行添加

赋值给healthIndicator

invoke,只需调用CompositeHealthIndicator#health 即可.代码如下:

public Health invoke() {

return this.healthIndicator.health();

}

1

2

3

endpoints.health.enabled= # Enable the endpoint.

endpoints.health.id= # Endpoint identifier.

endpoints.health.sensitive= # Mark if the endpoint exposes sensitive information.

endpoints.health.time-to-live=1000 # Time to live for cached result, in milliseconds.

1

2

3

4

@Bean

@ConditionalOnMissingBean

public HealthEndpoint healthEndpoint() {

return new HealthEndpoint(

this.healthAggregator == null ? new OrderedHealthAggregator()

: this.healthAggregator,

this.healthIndicators == null

? Collections.emptyMap()

: this.healthIndicators);

}

1

2

3

4

5

6

7

8

9

10

@Bean –> 注册1个id为healthEndpoint,类型为HealthEndpoint的bean

@ConditionalOnMissingBean –> 当beanFactory中不存在HealthEndpoint类型的bean 时生效.

默认使用的OrderedHealthAggregator

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:SpringMVC+Shiro的基本使用及功能介绍
下一篇:接口多个实现类的动态调用getBeansOfType
相关文章

 发表评论

暂时没有评论,来抢沙发吧~