详解SpringBoot健康检查的实现原理
SpringBoot自动装配的套路,直接看spring.factories文件,当我们使用的时候只需要引入如下依赖
org.springframework.boot spring-boot-starter-actuator
然后在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)
publicclassRabbitHealthIndicatorAutoConfigurationextends
CompositeHealthIndicatorConfiguration{
privatefinalMaprabbitTemplates;
publicRabbitHealthIndicatorAutoConfiguration(
MaprabbitTemplates){
this.rabbitTemplates=rabbitTemplates;
}
@Bean
@ConditionalOnMissingBean(name="rabbitHealthIndicator")
publicHealthIndicatorrabbitHealthIndicator(){
returncreateHealthIndicator(this.rabbitTemplates);
}
}
按照以往的惯例,先解析注解
- @ConditionalOnXXX系列又出现了,前两个就是说如果当前存在RabbitTemplate这个bean也就是说我们的项目中使用到了RabbitMQ才能进行下去
- @ConditionalOnEnabledHealthIndicator这个注解很明显是SpringBootactuator自定义的注解,看一下吧
@Conditional(OnEnabledHealthIndicatorCondition.class)
public@interfaceConditionalOnEnabledHealthIndicator{
Stringvalue();
}
classOnEnabledHealthIndicatorConditionextendsOnEndpointElementCondition{
OnEnabledHealthIndicatorCondition(){
super("management.health.",ConditionalOnEnabledHealthIndicator.class);
}
}
publicabstractclassOnEndpointElementConditionextendsSpringBootCondition{
privatefinalStringprefix;
privatefinalClassannotationType;
protectedOnEndpointElementCondition(Stringprefix,
ClassannotationType){
this.prefix=prefix;
this.annotationType=annotationType;
}
@Override
publicConditionOutcomegetMatchOutcome(ConditionContextcontext,
AnnotatedTypeMetadatametadata){
AnnotationAttributesannotationAttributes=AnnotationAttributes
.fromMap(metadata.getAnnotationAttributes(this.annotationType.getName()));
StringendpointName=annotationAttributes.getString("value");
ConditionOutcomeoutcome=getEndpointOutcome(context,endpointName);
if(outcome!=null){
returnoutcome;
}
returngetDefaultEndpointsOutcome(context);
}
protectedConditionOutcomegetEndpointOutcome(ConditionContextcontext,
StringendpointName){
Environmentenvironment=context.getEnvironment();
StringenabledProperty=this.prefix+endpointName+".enabled";
if(environment.containsProperty(enabledProperty)){
booleanmatch=environment.getProperty(enabledProperty,Boolean.class,true);
returnnewConditionOutcome(match,
ConditionMessage.forCondition(this.annotationType).because(
this.prefix+endpointName+".enabledis"+match));
}
returnnull;
}
protectedConditionOutcomegetDefaultEndpointsOutcome(ConditionContextcontext){
booleanmatch=Boolean.valueOf(context.getEnvironment()
.getProperty(this.prefix+"defaults.enabled","true"));
returnnewConditionOutcome(match,
ConditionMessage.forCondition(this.annotationType).because(
this.prefix+"defaults.enabledisconsidered"+match));
}
}
publicabstractclassSpringBootConditionimplementsCondition{
privatefinalLoglogger=LogFactory.getLog(getClass());
@Override
publicfinalbooleanmatches(ConditionContextcontext,
AnnotatedTypeMetadatametadata){
StringclassOrMethodName=getClassOrMethodName(metadata);
try{
ConditionOutcomeoutcome=getMatchOutcome(context,metadata);
logOutcome(classOrMethodName,outcome);
recordEvaluation(context,classOrMethodName,outcome);
returnoutcome.isMatch();
}
catch(NoClassDefFoundErrorex){
thrownewIllegalStateException(
"Couldnotevaluateconditionon"+classOrMethodName+"dueto"
+ex.getMessage()+"not"
+"found.Makesureyourownconfigurationdoesnotrelyon"
+"thatclass.Thiscanalsohappenifyouare"
+"@ComponentScanningaspringframeworkpackage(e.g.ifyou"
+"puta@ComponentScaninthedefaultpackagebymistake)",
ex);
}
catch(RuntimeExceptionex){
thrownewIllegalStateException(
"Errorprocessingconditionon"+getName(metadata),ex);
}
}
privatevoidrecordEvaluation(ConditionContextcontext,StringclassOrMethodName,
ConditionOutcomeoutcome){
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})
publicclassHealthIndicatorAutoConfiguration{
privatefinalHealthIndicatorPropertiesproperties;
publicHealthIndicatorAutoConfiguration(HealthIndicatorPropertiesproperties){
this.properties=properties;
}
@Bean
@ConditionalOnMissingBean({HealthIndicator.class,ReactiveHealthIndicator.class})
publicApplicationHealthIndicatorapplicationHealthIndicator(){
returnnewApplicationHealthIndicator();
}
@Bean
@ConditionalOnMissingBean(HealthAggregator.class)
publicOrderedHealthAggregatorhealthAggregator(){
OrderedHealthAggregatorhealthAggregator=newOrderedHealthAggregator();
if(this.properties.getOrder()!=null){
healthAggregator.setStatusOrder(this.properties.getOrder());
}
returnhealthAggregator;
}
}
首先这个类引入了配置文件HealthIndicatorProperties这个配置类是系统状态相关的配置
@ConfigurationProperties(prefix="management.health.status")
publicclassHealthIndicatorProperties{
privateListorder=null;
privatefinalMaphttpMapping=newHashMap<>();
}
接着就是注册了2个beanApplicationHealthIndicator和OrderedHealthAggregator这两个bean的作用稍后再说,现在回到RabbitHealthIndicatorAutoConfiguration类
@AutoConfigureAfter HealthIndicator publicabstractclassCompositeHealthIndicatorConfiguration{ @Autowired privateHealthAggregatorhealthAggregator; protectedHealthIndicatorcreateHealthIndicator(Map beans){ if(beans.size()==1){ returncreateHealthIndicator(beans.values().iterator().next()); } CompositeHealthIndicatorcomposite=newCompositeHealthIndicator( this.healthAggregator); for(Map.Entry entry:beans.entrySet()){ composite.addHealthIndicator(entry.getKey(), createHealthIndicator(entry.getValue())); } returncomposite; } @SuppressWarnings("unchecked") protectedHcreateHealthIndicator(Ssource){ Class>[]generics=ResolvableType .forClass(CompositeHealthIndicatorConfiguration.class,getClass()) .resolveGenerics(); Class indicatorClass=(Class )generics[0]; Class sourceClass=(Class)generics[1]; try{ returnindicatorClass.getConstructor(sourceClass).newInstance(source); } catch(Exceptionex){ thrownewIllegalStateException("Unabletocreateindicator"+indicatorClass +"forsource"+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的
publicfinalHealthhealth(){
Health.Builderbuilder=newHealth.Builder();
try{
doHealthCheck(builder);
}
catch(Exceptionex){
if(this.logger.isWarnEnabled()){
Stringmessage=this.healthCheckFailedMessage.apply(ex);
this.logger.warn(StringUtils.hasText(message)?message:DEFAULT_MESSAGE,
ex);
}
builder.down(ex);
}
returnbuilder.build();
}
//下方两个方法是由类RabbitHealthIndicator实现的
protectedvoiddoHealthCheck(Health.Builderbuilder)throwsException{
builder.up().withDetail("version",getVersion());
}
privateStringgetVersion(){
returnthis.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})
publicclassHealthEndpointAutoConfiguration{
publicHealthEndpointAutoConfiguration(){
}
}
这里重点的地方在于引入的HealthEndpointConfiguration这个类
@Configuration
classHealthEndpointConfiguration{
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
publicHealthEndpointhealthEndpoint(ApplicationContextapplicationContext){
returnnewHealthEndpoint(HealthIndicatorBeansComposite.get(applicationContext));
}
}
这个类只是构建了一个类HealthEndpoint,这个类我们可以理解为一个SpringMVC的Controller,也就是处理如下请求的
http://ip:port/actuator/health
那么首先看一下它的构造方法传入的是个啥对象吧
publicstaticHealthIndicatorget(ApplicationContextapplicationContext){
HealthAggregatorhealthAggregator=getHealthAggregator(applicationContext);
Mapindicators=newLinkedHashMap<>();
indicators.putAll(applicationContext.getBeansOfType(HealthIndicator.class));
if(ClassUtils.isPresent("reactor.core.publisher.Flux",null)){
newReactiveHealthIndicators().get(applicationContext)
.forEach(indicators::putIfAbsent);
}
CompositeHealthIndicatorFactoryfactory=newCompositeHealthIndicatorFactory();
returnfactory.createHealthIndicator(healthAggregator,indicators);
}
跟我们想象中的一样,就是通过Spring容器获取所有的HealthIndicator接口的实现类,我这里只有几个默认的和RabbitMQ
然后都放入了其中一个聚合的实现类CompositeHealthIndicator中
既然HealthEndpoint构建好了,那么只剩下最后一步处理请求了
@Endpoint(id="health")
publicclassHealthEndpoint{
privatefinalHealthIndicatorhealthIndicator;
@ReadOperation
publicHealthhealth(){
returnthis.healthIndicator.health();
}
}
刚刚我们知道,这个类是通过CompositeHealthIndicator构建的,所以health方法的实现就在这个类中
publicHealthhealth(){
Maphealths=newLinkedHashMap<>();
for(Map.Entryentry:this.indicators.entrySet()){
//循环调用
healths.put(entry.getKey(),entry.getValue().health());
}
//对结果集排序
returnthis.healthAggregator.aggregate(healths);
}
至此SpringBoot的健康检查实现原理全部解析完成
以上就是详解SpringBoot健康检查的实现原理的详细内容,更多关于SpringBoot健康检查实现原理的资料请关注毛票票其它相关文章!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。