欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解SpringBoot健康檢查的實現(xiàn)原理

 更新時間:2021年03月12日 09:45:42   作者:筱進客  
這篇文章主要介紹了詳解SpringBoot健康檢查的實現(xiàn)原理,幫助大家更好的理解和學習使用SpringBoot框架,感興趣的朋友可以了解下

SpringBoot自動裝配的套路,直接看 spring.factories 文件,當我們使用的時候只需要引入如下依賴

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

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

自動裝配

查看這個文件發(fā)現(xiàn)引入了很多的配置類,這里先關注一下 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 系列又出現(xiàn)了,前兩個就是說如果當前存在 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 的,這個方法首先會去環(huán)境變量中查找是否存在 management.health.rabbit.enabled 屬性,如果沒有的話則去查找 management.health.defaults.enabled 屬性,如果這個屬性還沒有的話則設置默認值為true

當這里返回true時整個 RabbitHealthIndicatorAutoConfiguration 類的自動配置才能繼續(xù)下去

  • @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 這個配置類是系統(tǒng)狀態(tài)相關的配置

@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的作用稍后再說,現(xiàn)在回到 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 方法執(zhí)行邏輯為:如果傳入的beans的size 為1,則調(diào)用 createHealthIndicator 創(chuàng)建 HealthIndicator 否則創(chuàng)建 CompositeHealthIndicator ,遍歷傳入的beans,依次創(chuàng)建 HealthIndicator ,加入到 CompositeHealthIndicator
  • 第二個 createHealthIndicator 的執(zhí)行邏輯為:獲得 CompositeHealthIndicatorConfiguration 中的泛型參數(shù)根據(jù)泛型參數(shù)H對應的class和S對應的class,在H對應的class中找到聲明了參數(shù)為S類型的構(gòu)造器進行實例化
  • 最后這里創(chuàng)建出來的bean為 RabbitHealthIndicator
  • 回憶起之前學習健康檢查的使用時,如果我們需要自定義健康檢查項時一般的操作都是實現(xiàn) HealthIndicator 接口,由此可以猜測 RabbitHealthIndicator 應該也是這樣做的。觀察這個類的繼承關系可以發(fā)現(xiàn)這個類繼承了一個實現(xiàn)實現(xiàn)此接口的類 AbstractHealthIndicator ,而RabbitMQ的監(jiān)控檢查流程則如下代碼所示
 //這個方法是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實現(xiàn)的
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 實現(xiàn)類,而負責檢查RabbitMQ健康不健康也是這個類來負責的。由此我們可以想象到如果當前環(huán)境存在MySQL、Redis、ES等情況應該也是這么個操作

那么接下來無非就是當有調(diào)用方訪問如下地址時,分別調(diào)用整個系統(tǒng)的所有的 HealthIndicator 的實現(xiàn)類的 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));
 }

}

這個類只是構(gòu)建了一個類 HealthEndpoint ,這個類我們可以理解為一個SpringMVC的Controller,也就是處理如下請求的

http://ip:port/actuator/health

那么首先看一下它的構(gòu)造方法傳入的是個啥對象吧

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 接口的實現(xiàn)類,我這里只有幾個默認的和RabbitMQ

然后都放入了其中一個聚合的實現(xiàn)類 CompositeHealthIndicator

既然 HealthEndpoint構(gòu)建好了,那么只剩下最后一步處理請求了

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

 private final HealthIndicator healthIndicator;

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

}

剛剛我們知道,這個類是通過 CompositeHealthIndicator 構(gòu)建的,所以 health 方法的實現(xiàn)就在這個類中

public Health health() {
  Map<String, Health> healths = new LinkedHashMap<>();
  for (Map.Entry<String, HealthIndicator> entry : this.indicators.entrySet()) {
   //循環(huán)調(diào)用
   healths.put(entry.getKey(), entry.getValue().health());
  }
  //對結(jié)果集排序
  return this.healthAggregator.aggregate(healths);
 }

至此SpringBoot的健康檢查實現(xiàn)原理全部解析完成

以上就是詳解SpringBoot健康檢查的實現(xiàn)原理的詳細內(nèi)容,更多關于SpringBoot健康檢查實現(xiàn)原理的資料請關注腳本之家其它相關文章!

相關文章

  • SpringBoot3使用Jasypt實現(xiàn)配置文件信息加密的方法

    SpringBoot3使用Jasypt實現(xiàn)配置文件信息加密的方法

    對于一些單體項目而言,在沒有使用SpringCloud的情況下,配置文件中包含著大量的敏感信息,如果這些信息泄露出去將會對企業(yè)的資產(chǎn)產(chǎn)生重大威脅,因此,對配置文件中的敏感信息加密是一件極其必要的事,所以本文介紹了SpringBoot3使用Jasypt實現(xiàn)配置文件信息加密的方法
    2024-07-07
  • Java?SSM框架講解

    Java?SSM框架講解

    這篇文章主要介紹了什么是SSM框架,SSM框架是spring、spring?MVC?、和mybatis框架的整合,是標準的MVC模式。想進一步了解的同學可以詳細參考本文
    2023-03-03
  • Mybatis動態(tài)sql中@Param使用詳解

    Mybatis動態(tài)sql中@Param使用詳解

    這篇文章主要介紹了Mybatis動態(tài)sql中@Param使用詳解,當方法的參數(shù)為非自定義pojo類型,且使用了動態(tài)sql,那么就需要在參數(shù)前加上@Param注解,需要的朋友可以參考下
    2023-10-10
  • Spring中的事務管理實例詳解

    Spring中的事務管理實例詳解

    這篇文章主要介紹了Spring中的事務管理,以實例形式詳細分析了事務的概念與特性以及事物管理的具體用法,需要的朋友可以參考下
    2014-11-11
  • SpringMVC中使用Thymeleaf模板引擎實例代碼

    SpringMVC中使用Thymeleaf模板引擎實例代碼

    這篇文章主要介紹了SpringMVC中使用Thymeleaf模板引擎實例代碼,分享了相關代碼示例,小編覺得還是挺不錯的,具有一定借鑒價值,需要的朋友可以參考下
    2018-02-02
  • JAVA數(shù)組練習題實例講解

    JAVA數(shù)組練習題實例講解

    這篇文章主要給大家介紹了關于JAVA數(shù)組練習題的相關資料,這是個人總結(jié)的一些關于java數(shù)組的練習題,文中通過代碼實例介紹的非常詳細,需要的朋友可以參考下
    2023-08-08
  • SpringCloud-Nacos服務注冊與發(fā)現(xiàn)方式

    SpringCloud-Nacos服務注冊與發(fā)現(xiàn)方式

    這篇文章主要介紹了SpringCloud-Nacos服務注冊與發(fā)現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • 詳解Spring Boot 目錄文件結(jié)構(gòu)

    詳解Spring Boot 目錄文件結(jié)構(gòu)

    這篇文章主要介紹了Spring Boot 目錄文件結(jié)構(gòu)的相關資料,文中示例代碼非常詳細,幫助大家更好的理解和學習,感興趣的朋友可以了解下
    2020-07-07
  • idea集成shell運行環(huán)境以及shell輸出中文亂碼的解決

    idea集成shell運行環(huán)境以及shell輸出中文亂碼的解決

    這篇文章主要介紹了idea集成shell運行環(huán)境以及shell輸出中文亂碼的解決,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • Java Math類的三個方法ceil,floor,round用法

    Java Math類的三個方法ceil,floor,round用法

    這篇文章主要介紹了Java Math類的三個方法ceil,floor,round用法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07

最新評論