我的编程空间,编程开发者的网络收藏夹
学习永远不晚

详解SpringBoot健康检查的实现原理

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

详解SpringBoot健康检查的实现原理

SpringBoot自动装配的套路,直接看 spring.factories 文件,当我们使用的时候只需要引入如下依赖


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

然后在 org.springframework.boot.spring-boot-actuator-autoconfigure 包下去就可以找到这个文件

自动装配

查看这个文件发现引入了很多的配置类,这里先关注一下 XXXHealthIndicatorAutoConfiguration 系列的类,这里咱们拿第一个 RabbitHealthIndicatorAutoConfiguration 为例来解析一下。看名字就知道这个是RabbitMQ的健康检查的自动配置类


@Configuration
@ConditionalOnClass(RabbitTemplate.class)
@ConditionalOnBean(RabbitTemplate.class)
@ConditionalOnEnabledHealthIndicator("rabbit")
@AutoConfigureBefore(HealthIndicatorAutoConfiguration.class)
@AutoConfigureAfter(RabbitAutoConfiguration.class)
public class RabbitHealthIndicatorAutoConfiguration extends
  CompositeHealthIndicatorConfiguration<RabbitHealthIndicator, RabbitTemplate> {

 private final Map<String, RabbitTemplate> rabbitTemplates;

 public RabbitHealthIndicatorAutoConfiguration(
   Map<String, RabbitTemplate> rabbitTemplates) {
  this.rabbitTemplates = rabbitTemplates;
 }

 @Bean
 @ConditionalOnMissingBean(name = "rabbitHealthIndicator")
 public HealthIndicator rabbitHealthIndicator() {
  return createHealthIndicator(this.rabbitTemplates);
 }
}

按照以往的惯例,先解析注解

  • @ConditionalOnXXX 系列又出现了,前两个就是说如果当前存在 RabbitTemplate 这个bean也就是说我们的项目中使用到了RabbitMQ才能进行下去
  • @ConditionalOnEnabledHealthIndicator 这个注解很明显是SpringBoot actuator自定义的注解,看一下吧

@Conditional(OnEnabledHealthIndicatorCondition.class)
public @interface ConditionalOnEnabledHealthIndicator {
 String value();
}
class OnEnabledHealthIndicatorCondition extends OnEndpointElementCondition {

 OnEnabledHealthIndicatorCondition() {
  super("management.health.", ConditionalOnEnabledHealthIndicator.class);
 }

}
public abstract class OnEndpointElementCondition extends SpringBootCondition {

 private final String prefix;

 private final Class<? extends Annotation> annotationType;

 protected OnEndpointElementCondition(String prefix,
   Class<? extends Annotation> annotationType) {
  this.prefix = prefix;
  this.annotationType = annotationType;
 }

 @Override
 public ConditionOutcome getMatchOutcome(ConditionContext context,
   AnnotatedTypeMetadata metadata) {
  AnnotationAttributes annotationAttributes = AnnotationAttributes
    .fromMap(metadata.getAnnotationAttributes(this.annotationType.getName()));
  String endpointName = annotationAttributes.getString("value");
  ConditionOutcome outcome = getEndpointOutcome(context, endpointName);
  if (outcome != null) {
   return outcome;
  }
  return getDefaultEndpointsOutcome(context);
 }

 protected ConditionOutcome getEndpointOutcome(ConditionContext context,
   String endpointName) {
  Environment environment = context.getEnvironment();
  String enabledProperty = this.prefix + endpointName + ".enabled";
  if (environment.containsProperty(enabledProperty)) {
   boolean match = environment.getProperty(enabledProperty, Boolean.class, true);
   return new ConditionOutcome(match,
     ConditionMessage.forCondition(this.annotationType).because(
       this.prefix + endpointName + ".enabled is " + match));
  }
  return null;
 }

 protected ConditionOutcome getDefaultEndpointsOutcome(ConditionContext context) {
  boolean match = Boolean.valueOf(context.getEnvironment()
    .getProperty(this.prefix + "defaults.enabled", "true"));
  return new ConditionOutcome(match,
    ConditionMessage.forCondition(this.annotationType).because(
      this.prefix + "defaults.enabled is considered " + match));
 }

}
public abstract class SpringBootCondition implements Condition {

 private final Log logger = LogFactory.getLog(getClass());

 @Override
 public final boolean matches(ConditionContext context,
   AnnotatedTypeMetadata metadata) {
  String classOrMethodName = getClassOrMethodName(metadata);
  try {
   ConditionOutcome outcome = getMatchOutcome(context, metadata);
   logOutcome(classOrMethodName, outcome);
   recordEvaluation(context, classOrMethodName, outcome);
   return outcome.isMatch();
  }
  catch (NoClassDefFoundError ex) {
   throw new IllegalStateException(
     "Could not evaluate condition on " + classOrMethodName + " due to "
       + ex.getMessage() + " not "
       + "found. Make sure your own configuration does not rely on "
       + "that class. This can also happen if you are "
       + "@ComponentScanning a springframework package (e.g. if you "
       + "put a @ComponentScan in the default package by mistake)",
     ex);
  }
  catch (RuntimeException ex) {
   throw new IllegalStateException(
     "Error processing condition on " + getName(metadata), ex);
  }
 }
 private void recordEvaluation(ConditionContext context, String classOrMethodName,
   ConditionOutcome outcome) {
  if (context.getBeanFactory() != null) {
   ConditionEvaluationReport.get(context.getBeanFactory())
     .recordConditionEvaluation(classOrMethodName, this, outcome);
  }
 }
}

上方的入口方法是 SpringBootCondition 类的 matches 方法, getMatchOutcome 这个方法则是子类 OnEndpointElementCondition 的,这个方法首先会去环境变量中查找是否存在 management.health.rabbit.enabled 属性,如果没有的话则去查找 management.health.defaults.enabled 属性,如果这个属性还没有的话则设置默认值为true

当这里返回true时整个 RabbitHealthIndicatorAutoConfiguration 类的自动配置才能继续下去

  • @AutoConfigureBefore 既然这样那就先看看类 HealthIndicatorAutoConfiguration 都是干了啥再回来吧

@Configuration
@EnableConfigurationProperties({ HealthIndicatorProperties.class })
public class HealthIndicatorAutoConfiguration {

 private final HealthIndicatorProperties properties;

 public HealthIndicatorAutoConfiguration(HealthIndicatorProperties properties) {
  this.properties = properties;
 }

 @Bean
 @ConditionalOnMissingBean({ HealthIndicator.class, ReactiveHealthIndicator.class })
 public ApplicationHealthIndicator applicationHealthIndicator() {
  return new ApplicationHealthIndicator();
 }

 @Bean
 @ConditionalOnMissingBean(HealthAggregator.class)
 public OrderedHealthAggregator healthAggregator() {
  OrderedHealthAggregator healthAggregator = new OrderedHealthAggregator();
  if (this.properties.getOrder() != null) {
   healthAggregator.setStatusOrder(this.properties.getOrder());
  }
  return healthAggregator;
 }

}

首先这个类引入了配置文件 HealthIndicatorProperties 这个配置类是系统状态相关的配置


@ConfigurationProperties(prefix = "management.health.status")
public class HealthIndicatorProperties {

 private List<String> order = null;

 private final Map<String, Integer> httpMapping = new HashMap<>();
}

接着就是注册了2个bean ApplicationHealthIndicatorOrderedHealthAggregator 这两个bean的作用稍后再说,现在回到 RabbitHealthIndicatorAutoConfiguration 类


@AutoConfigureAfter
HealthIndicator
public abstract class CompositeHealthIndicatorConfiguration<H extends HealthIndicator, S> {

 @Autowired
 private HealthAggregator healthAggregator;

 protected HealthIndicator createHealthIndicator(Map<String, S> beans) {
  if (beans.size() == 1) {
   return createHealthIndicator(beans.values().iterator().next());
  }
  CompositeHealthIndicator composite = new CompositeHealthIndicator(
    this.healthAggregator);
  for (Map.Entry<String, S> entry : beans.entrySet()) {
   composite.addHealthIndicator(entry.getKey(),
     createHealthIndicator(entry.getValue()));
  }
  return composite;
 }

 @SuppressWarnings("unchecked")
 protected H createHealthIndicator(S source) {
  Class<?>[] generics = ResolvableType
    .forClass(CompositeHealthIndicatorConfiguration.class, getClass())
    .resolveGenerics();
  Class<H> indicatorClass = (Class<H>) generics[0];
  Class<S> sourceClass = (Class<S>) generics[1];
  try {
   return indicatorClass.getConstructor(sourceClass).newInstance(source);
  }
  catch (Exception ex) {
   throw new IllegalStateException("Unable to create indicator " + indicatorClass
     + " for source " + sourceClass, ex);
  }
 }

}
  • 首先这里注入了一个对象 HealthAggregator ,这个对象就是刚才注册的 OrderedHealthAggregator
  • 第一个 createHealthIndicator 方法执行逻辑为:如果传入的beans的size 为1,则调用 createHealthIndicator 创建 HealthIndicator 否则创建 CompositeHealthIndicator ,遍历传入的beans,依次创建 HealthIndicator ,加入到 CompositeHealthIndicator
  • 第二个 createHealthIndicator 的执行逻辑为:获得 CompositeHealthIndicatorConfiguration 中的泛型参数根据泛型参数H对应的class和S对应的class,在H对应的class中找到声明了参数为S类型的构造器进行实例化
  • 最后这里创建出来的bean为 RabbitHealthIndicator
  • 回忆起之前学习健康检查的使用时,如果我们需要自定义健康检查项时一般的操作都是实现 HealthIndicator 接口,由此可以猜测 RabbitHealthIndicator 应该也是这样做的。观察这个类的继承关系可以发现这个类继承了一个实现实现此接口的类 AbstractHealthIndicator ,而RabbitMQ的监控检查流程则如下代码所示

 //这个方法是AbstractHealthIndicator的
public final Health health() {
  Health.Builder builder = new Health.Builder();
  try {
   doHealthCheck(builder);
  }
  catch (Exception ex) {
   if (this.logger.isWarnEnabled()) {
    String message = this.healthCheckFailedMessage.apply(ex);
    this.logger.warn(StringUtils.hasText(message) ? message : DEFAULT_MESSAGE,
      ex);
   }
   builder.down(ex);
  }
  return builder.build();
 }
//下方两个方法是由类RabbitHealthIndicator实现的
protected void doHealthCheck(Health.Builder builder) throws Exception {
  builder.up().withDetail("version", getVersion());
 }

 private String getVersion() {
  return this.rabbitTemplate.execute((channel) -> channel.getConnection()
    .getServerProperties().get("version").toString());
 }

健康检查

上方一系列的操作之后,其实就是搞出了一个RabbitMQ的 HealthIndicator 实现类,而负责检查RabbitMQ健康不健康也是这个类来负责的。由此我们可以想象到如果当前环境存在MySQL、Redis、ES等情况应该也是这么个操作

那么接下来无非就是当有调用方访问如下地址时,分别调用整个系统的所有的 HealthIndicator 的实现类的 health 方法即可了

http://ip:port/actuator/health

HealthEndpointAutoConfiguration

上边说的这个操作过程就在类 HealthEndpointAutoConfiguration 中,这个配置类同样也是在 spring.factories 文件中引入的


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

这里重点的地方在于引入的 HealthEndpointConfiguration 这个类


@Configuration
class HealthEndpointConfiguration {

 @Bean
 @ConditionalOnMissingBean
 @ConditionalOnEnabledEndpoint
 public HealthEndpoint healthEndpoint(ApplicationContext applicationContext) {
  return new HealthEndpoint(HealthIndicatorBeansComposite.get(applicationContext));
 }

}

这个类只是构建了一个类 HealthEndpoint ,这个类我们可以理解为一个SpringMVC的Controller,也就是处理如下请求的


http://ip:port/actuator/health

那么首先看一下它的构造方法传入的是个啥对象吧


public static HealthIndicator get(ApplicationContext applicationContext) {
  HealthAggregator healthAggregator = getHealthAggregator(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);
  }
  CompositeHealthIndicatorFactory factory = new CompositeHealthIndicatorFactory();
  return factory.createHealthIndicator(healthAggregator, indicators);
 }

跟我们想象中的一样,就是通过Spring容器获取所有的 HealthIndicator 接口的实现类,我这里只有几个默认的和RabbitMQ

然后都放入了其中一个聚合的实现类 CompositeHealthIndicator

既然 HealthEndpoint构建好了,那么只剩下最后一步处理请求了


@Endpoint(id = "health")
public class HealthEndpoint {

 private final HealthIndicator healthIndicator;

 @ReadOperation
 public Health health() {
  return this.healthIndicator.health();
 }

}

刚刚我们知道,这个类是通过 CompositeHealthIndicator 构建的,所以 health 方法的实现就在这个类中


public Health health() {
  Map<String, Health> healths = new LinkedHashMap<>();
  for (Map.Entry<String, HealthIndicator> entry : this.indicators.entrySet()) {
   //循环调用
   healths.put(entry.getKey(), entry.getValue().health());
  }
  //对结果集排序
  return this.healthAggregator.aggregate(healths);
 }

至此SpringBoot的健康检查实现原理全部解析完成

以上就是详解SpringBoot健康检查的实现原理的详细内容,更多关于SpringBoot健康检查实现原理的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

详解SpringBoot健康检查的实现原理

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

nginx增加健康检查接口的实现示例

这篇文章提供了nginx健康检查接口的实现示例。通过使用ngx_http_realip_module和ngx_http_stub_status_module,可以配置一个自定义接口,允许外部系统检查服务器的可用性。该接口可以通过/healthcheck路径访问,并可以通过限制IP访问来提高安全性。监控集成和性能优化等其他考虑因素也在文中讨论。
nginx增加健康检查接口的实现示例
2024-04-02

SpringBoot-Admin如何实现微服务监控+健康检查+钉钉告警

小编给大家分享一下SpringBoot-Admin如何实现微服务监控+健康检查+钉钉告警,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!基于SpringCloud微服务平台,进行服务实例监控及健康检查,注册中心为eureka
2023-06-25

JavaAQS的实现原理详解

这篇文章主要借助了ReentrantLock来带大家搞清楚AQS的实现原理,文中的示例代码讲解详细,具有一定的学习价值,感兴趣的小伙伴可以了解一下
2023-05-14

详解IOS WebRTC的实现原理

目录概述P2P连接模式WebRTC的服务器与信令WebRTC的NAT/防火墙穿越技术概述 它在2011年5月开放了工程的源代码,在行业内得到了广泛的支持和应用,成为下一代视频通话的标准。 WebRTC的音视频通信是基于P2P,那么什么是P2
2022-05-15

PHPLaravel门面的实现原理详解

在Laravel中,门面为应用服务容器中绑定的类提供了一个“静态”接口,使得我们可以不用new这些类出来,就可以直接通过静态接口调用这些类中的方法。本文就来详细聊聊Laravel门面的实现原理,希望对大家有所帮助
2023-02-09

OpenMP Parallel Construct的实现原理详解

在本篇文章当中我们将主要分析 OpenMP 当中的 parallel construct 具体时如何实现的,以及这个 construct 调用了哪些运行时库函数,并且详细分析这期间的参数传递,需要的可以参考一下
2023-01-28

C++中function的实现原理详解

类模版std::function是一种通用、多态的函数封装。function的实例可以对任何可以调用的目标实体进行存储、复制、和调用操作。本文主要聊聊它的实现原理,需要的可以参考一下
2022-12-09

详解Android中Handler的实现原理

在 Android 中,只有主线程才能操作 UI,但是主线程不能进行耗时操作,否则会阻塞线程,产生 ANR 异常,所以常常把耗时操作放到其它子线程进行。如果在子线程中需要更新 UI,一般是通过 Handler 发送消息,主线程接受消息并且进
2022-06-06

详解HTTPS 的原理和 NodeJS 的实现

基本原理HTTP协议采用明文传输数据,当涉及敏感信息的传送时,极有可能会受到窃听或者中间人的攻击。HTTPS是HTTP与SSL/TLS的组合,即使用加密通讯以及网络服务器的身份鉴定来进行信息的安全传输。其核心有二:使用证书对服务器及请求端的
2022-06-04

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录