解析springBoot-actuator项目构造中health端点工作原理

 更新时间:2022年02月25日 15:29:46   作者:kl  
这篇文章主要介绍了springBoot-actuator中health端点工作原理,对spring-boot-actuator的项目构造,工作原理进行了全面的梳理,侧重health健康检查部分

前言

最近在一个webflux项目中使用spring-boot-actuator提供的健康检查端点时出了点问题,故对spring-boot-actuator的项目构造,工作原理进行了全面的梳理,标题之所以写明health的工作原理,是因为spring-boot-actuator着实是个大工程,除了提供health端点,还包含了env,log,dump等诸多功能,下面会侧重health健康检查部分,详细探索下。

actuator功能和集成分离

一般在spring boot中使用actuator的时候,会引入下面这个starter

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

在这个starter里面会包含两个依赖,一个是功能实现spring-boot-actuator

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-actuator</artifactId>
      <version>2.1.0.RELEASE</version>
</dependency>

还有一个是和spring boot做集成的config配置,以及Bean自动装配的依赖,如下:

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-actuator-autoconfigure</artifactId>
      <version>2.1.0.RELEASE</version>
</dependency>

actuator自动装载

找到spring-boot-actuator-autoconfigure依赖,定位到org.springframework.boot.actuate.autoconfigure.health包下,有如下的结构:

如箭头所指向的HealthEndpointAutoConfiguration.java自动配置类就是actuator中health的启动入口,源码如下:

@Configuration
@EnableConfigurationProperties({ HealthEndpointProperties.class,
      HealthIndicatorProperties.class })
@AutoConfigureAfter(HealthIndicatorAutoConfiguration.class)
@Import({ HealthEndpointConfiguration.class,
      HealthEndpointWebExtensionConfiguration.class })
public class HealthEndpointAutoConfiguration {

}

阅读上面代码需要了解spring boot自动装载机制,这里简单解读下,首先@Configuration开启了配置特性,@EnableConfigurationProperties启用了健康检查端点、健康检查指示器的属性配置,@AutoConfigureAfter定义了健康检查自动装配要在HealthIndicatorAutoConfiguration之后,@Import包含了两个自动装载类,下面详解下三个主要的配置类:

健康检查指示器配置

HEALTHINDICATORAUTOCONFIGURATION

健康检查指示器定义了哪些组件需要被检测,常见的指示器有JDBC数据源(DataSourceHealthIndicator.java),磁盘健康指示器(DiskSpaceHealthIndicator.java)等。每个指示器对应了一个自动装配的类,根据Bean初始化条件去初始化,如JDBC数据源的初始化条件如下:

当上Spring上下文中包含DataSource实施,即开启JDBC健康检查指示器。这些指示器最终会被收集到指示器注册器中DefaultHealthIndicatorRegistry.java

健康检查指示器配置就是完成了指示器注册器的初始化动作,代码如:

@Bean
	@ConditionalOnMissingBean(HealthIndicatorRegistry.class)
	public HealthIndicatorRegistry healthIndicatorRegistry(
			ApplicationContext applicationContext) {
		return HealthIndicatorRegistryBeans.get(applicationContext);
	}
	public static HealthIndicatorRegistry get(ApplicationContext applicationContext) {
		Map<String, HealthIndicator> indicators = new LinkedHashMap<>();
		indicators.putAll(applicationContext.getBeansOfType(HealthIndicator.class));
		if (ClassUtils.isPresent("reactor.core.publisher.Flux", null)) {
			new ReactiveHealthIndicators().get(applicationContext)
					.forEach(indicators::putIfAbsent);
		}
		HealthIndicatorRegistryFactory factory = new HealthIndicatorRegistryFactory();
		return factory.createHealthIndicatorRegistry(indicators);
	}

可以看到,就是去Spring 应用上下文ApplicationContext中找Bean类型是HealthIndicator.class的实例,如果项目中使用了webFlux,会额外注册Reactive相关的指示器

健康检查端点配置

端点配置比较简单,就是实例化一个HealthEndpoint.java,最终健康检查所有的功能入口都会被抽象汇聚到这个实例里,配置代码如下:

@Configuration
@ConditionalOnSingleCandidate(HealthIndicatorRegistry.class)
class HealthEndpointConfiguration {

	@Bean
	@ConditionalOnMissingBean
	@ConditionalOnEnabledEndpoint
	public HealthEndpoint healthEndpoint(HealthAggregator healthAggregator,
			HealthIndicatorRegistry registry) {
		return new HealthEndpoint(
				new CompositeHealthIndicator(healthAggregator, registry));
	}
}

可以看到前提条件是已经有一个健康指示注册器单例实例了

health健康检查实现

在spring-boot-actuator中,定义了@Endpoint注解,用以声明一个actuator端点,health端点也是一样,通过@Endpoint(id="health")暴露了/actuator/health接口。并通过@ReadOperation注解映射了三个方法,如下:

Health health()

       访问http://127.0.0.1:8080/actuator/health时会执行这个方法,调用所有的健康指示器实现,并返回结果

Health healthForComponent(@Selector String component)

       访问http://127.0.0.1:8080/actuator/health/${component}时会执行这个方法,会根据component的值,找到相关的指示器,并检查返回结果

Health healthForComponentInstance(@Selector String component, @Selector String instance)

       访问http://127.0.0.1:8080/actuator/health/${component}/${instance}时会执行这个方法,会根据component、instance的值,找到相关的指示器,并检查返回结果。其中component是组件的name,instance是组件实例的name值。component的name由执行器组件配置类上的注解@ConditionalOnEnabledHealthIndicator来指定,目前包含的指示器组件有如:

我们以redis的指示器RedisHealthIndicator.java来看下,最终指示器是怎么判断组件是否健康的,实现如:

public class RedisHealthIndicator extends AbstractHealthIndicator {
	static final String VERSION = "version";
	static final String REDIS_VERSION = "redis_version";
	private final RedisConnectionFactory redisConnectionFactory;
	public RedisHealthIndicator(RedisConnectionFactory connectionFactory) {
		super("Redis health check failed");
		Assert.notNull(connectionFactory, "ConnectionFactory must not be null");
		this.redisConnectionFactory = connectionFactory;
	}
	@Override
	protected void doHealthCheck(Health.Builder builder) throws Exception {
		RedisConnection connection = RedisConnectionUtils
				.getConnection(this.redisConnectionFactory);
		try {
			if (connection instanceof RedisClusterConnection) {
				ClusterInfo clusterInfo = ((RedisClusterConnection) connection)
						.clusterGetClusterInfo();
				builder.up().withDetail("cluster_size", clusterInfo.getClusterSize())
						.withDetail("slots_up", clusterInfo.getSlotsOk())
						.withDetail("slots_fail", clusterInfo.getSlotsFail());
			}
			else {
				Properties info = connection.info();
				builder.up().withDetail(VERSION, info.getProperty(REDIS_VERSION));
			}
		}
		finally {
			RedisConnectionUtils.releaseConnection(connection,
					this.redisConnectionFactory);
		}
	}
}

可以看到,首先判断了连接的类型时集群模式还是单机模式,然后分别调用了info指令,去拿redis的版本信息

自定义健康检查指示器

了解到这里,自定义实现一个组件的健康检查就容易了。首先自定义指示器继承AbstractHealthIndicator类,实现doHealthCheck方法,然后定义自定义指示器的配置类继承CompositeHealthIndicatorConfiguration就ok了,伪代码如下:

@ConditionalOnEnabledHealthIndicator("myDb")
@Configuration
public class MyHealthIndicatorAutoConfiguration extends CompositeHealthIndicatorConfiguration<DataSourceHealthIndicator,DataSource> {
    @Bean
    @ConditionalOnMissingBean(name = "myDbHealthIndicator")
    public HealthIndicator dbHealthIndicator() {
        return new MyHealthIndicator();
    }
}
class MyHealthIndicator extends AbstractHealthIndicator{
    @Override
    protected void doHealthCheck(Health.Builder builder) {
        //这里定义组建健康的逻辑
        builder.up();
    }
}

health其他使用细节

除了上面提到的健康检查不只/actuator/health端点,还能指定组件检查外,还提供了很多可以通过配置控制的特性,如指示器的开关,什么时候显示健康检查详情等,具体如下:

management.endpoints.web.base-path=/actuator
management.endpoint.health.enabled=true
management.endpoint.health.show-details=never
management.endpoint.health.roles=admin
management.health.db.enabled=true

文末结语

本着用好每一个组件,不放过任何一个实现细节的原则,对spring-boot-actuator中的health实现原理剖析了下。不过actuator真的是个大家伙,光健康检查指示器就有18个实现,特别要说明下的是,针对health,在做健康检查指示器时,会区分web和webFlux。主要原因是在webFlux的环境下,相关的组件也会出Reactive的客户端,比如redis在webFlux下就可以使用Lettuce。

以上就是解析springBoot-actuator中health端点工作原理的详细内容,更多关于springBoot-actuator中health原理的资料请关注脚本之家其它相关文章!

相关文章

  • 详解maven中央仓库连不上的解决办法

    详解maven中央仓库连不上的解决办法

    这篇文章主要介绍了详解maven中央仓库连不上的解决办法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • 基于JavaMail实现邮件发送

    基于JavaMail实现邮件发送

    这篇文章主要为大家详细介绍了基于JavaMail实现邮件发送功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-03-03
  • Java-Redis-Redisson分布式锁的功能使用及实现

    Java-Redis-Redisson分布式锁的功能使用及实现

    这篇文章主要介绍了Java-Redis-Redisson-分布式锁的功能使用及实现,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-08-08
  • 深入java垃圾回收的详解

    深入java垃圾回收的详解

    本篇文章是对java垃圾回收进行了详细的分析介绍,需要的朋友参考下
    2013-06-06
  • 浅谈Spring AOP中args()和argNames的含义

    浅谈Spring AOP中args()和argNames的含义

    这篇文章主要介绍了Spring AOP中args()和argNames的含义,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • RocketMQ producer发送者浅析

    RocketMQ producer发送者浅析

    RocketMQ生产者是一种高性能、可靠的消息发送者,能够将消息快速、可靠地发送到RocketMQ消息队列中。它具有多种消息发送模式和消息发送方式,可以根据不同的业务需求进行灵活配置
    2023-04-04
  • javaWeb使用servlet搭建服务器入门

    javaWeb使用servlet搭建服务器入门

    这篇文章主要为大家详细介绍了javaWeb使用servlet搭建服务器入门,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-11-11
  • 基于selenium 获取新页面元素失败的解决方法

    基于selenium 获取新页面元素失败的解决方法

    今天小编就为大家分享一篇基于selenium 获取新页面元素失败的解决方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-05-05
  • Java复杂链表的复制详解

    Java复杂链表的复制详解

    复杂链表指的是一个链表有若干个结点,每个结点有一个数据域用于存放数据,还有两个指针域,其中一个指向下一个节点,还有一个随机指向当前复杂链表中的任意一个节点或者是一个空结点,我们来探究一下在Java中复杂链表的复制
    2022-01-01
  • Java数组常用排序算法实例小结

    Java数组常用排序算法实例小结

    这篇文章主要介绍了Java数组常用排序算法,结合实例形式总结分析了java数组常用的4种排序算法,包括冒泡排序、数组递增排序、快速排序及选择排序,需要的朋友可以参考下
    2017-12-12

最新评论