作者:404,公众号404P,转载请注明出处。

前言

SOFABoot是蚂蚁金服的开源框架,在原有Spring Boot的基础上增强了不少能力,例如Readiness Check,类隔离,日志空间隔离等能力。除此之外,SOFABoot还可以方便的整合SOFA技术栈所包含的各类中间件。如果想要对SOFABoot有体感,可以参考这里快速构建一个SOFABoot的应用

本文来聊聊SOFABoot新增的Readiness健康检查机制。主要内容有以下几点:

  • liveness 和 readiness 的含义和区别
  • SOFABoot项目如何使用readiness的能力
  • SOFABoot是如何实现readiness的

一 Liveness 和Readiness

服务的健康检查,是微服务的基础能力,在微服务的运行时期定时地检查服务健康状态,为熔断降级等提供决策依据。那么说到健康检查,这里提出两个概念:liveness和readiness。这两个概念什么意思呢?有何区别呢?我们先看看在容器编排领域,k8s官网是在什么场景下提到这两个词的。

The kubelet uses liveness probes to know when to restart a Container. For example, liveness probes could catch a deadlock, where an application is running, but unable to make progress. Restarting a Container in such a state can help to make the application more available despite bugs.

The kubelet uses readiness probes to know when a Container is ready to start accepting traffic. A Pod is considered ready when all of its Containers are ready. One use of this signal is to control which Pods are used as backends for Services. When a Pod is not ready, it is removed from Service load balancers.

kubelet用liveness探针来检测应用在运行的过程中何时该重启一个容器。例如,liveness探针检测到一个应用在运行中陷入死锁状态,毫无进展,那么这个时候会重启容器暂时避免这种无解的运行状态,保持应用的正常运行。

kuelet用readiness探针来检测何时一个容器可以接受业务流量。当一个Pod中的所有容器都准备就绪了,这个Pod才被认为是准备就绪的,这个时候才会将容器放入到Service的负载均衡池中,对外提供服务。

所以,liveness的职责是在服务运行期,已经在跑业务时,定时检查服务是否正常;而readiness的职责则是在应用服务运行之前,判断该服务是否准备就绪,如果服务就绪了,负载均衡就可以将业务流量引入到该服务了。服务就绪往往有很多需要判断的,例如:各项配置是否加载完毕。如果这些提供服务前的准备工作未就绪,这个时候把流量放进来,就会有大量报错。

二 SOFABoot项目中使用Readiness Check

Readiness Check 在 SOFABoot中是个可选能力,通过starter的方式提供,如果需要使用,引入下方依赖即可:

<dependency>
<groupId>com.alipay.sofa</groupId>
<artifactId>healthcheck-sofa-boot-starter</artifactId>
</dependency>

该starter包含了SpringBoot的健康检查spring-boot-starter-actuator。

在应用启动时,即可启动Readiness检查。

三 SOFABoot如何实现Readiness Check

我们到healthcheck-sofa-boot-starter对应的spring.factories文件看看有哪些自定义bean,其配置如下:

org.springframework.context.ApplicationContextInitializer=\
com.alipay.sofa.healthcheck.initializer.SofaBootHealthCheckInitializer org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.alipay.sofa.healthcheck.configuration.SofaBootHealthCheckAutoConfiguration

配置了两个SOFABoot的实现类,一个是用应用初始化组件,一个是SOFABoot健康检查需要的配置类。

1 应用初始化

SofaBootHealthCheckInitializer

这个是SOFABoot对于ApplicationContextInitializer的实现,这个接口的主要职责是:在springcontext 刷新(refresh)之前,调用该接口的initialize做前置的初始化操作,我们看看SOFABoot初始化做了什么事情:

public class SofaBootHealthCheckInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
Environment environment = applicationContext.getEnvironment();
if (SOFABootEnvUtils.isSpringCloudBootstrapEnvironment(environment)) {
return;
}
LOGGER.info("SOFABoot HealthCheck Starting!");
}
}

初始化的时候,判断了当前是否为SpringCloud的引导上下文,如果是的话,则返回,不打印日志,如果不是的话,则打印日志。

这是什么逻辑? 首先,初始化逻辑里只做了一件事情:打印日志,并且是在非SpringCloud环境下才打印日志?其实这是一个兼容逻辑。在SpringCloud环境下,自定义的initializer会被调用两次initialize方法(参考 # issue1151 & # issue 232),SpringCloud会加载一个引导上下文(bootstrap context)进来,我们自己的应用程序会加载应用上下文(application context)进来,这两个同时存在,intialzie会调用两次。

isSpringCloudBootstrapEnvironment方法就是为了区分是否为SpringCloud加载进来的引导上下文,从而屏蔽掉这次initialize执行,确保日志只会在应用上下文时输出,该方法主要是通过是否存在SpringCloud中的特定类来识别是否引入SpringCloud,这里就不赘述了,读者可自行查看。

2 SOFABoot实现Readiness的核心逻辑

2.1 核心Bean介绍

SofaBootHealthCheckAutoConfiguration

要搞清楚实现Readiness的核心实现,我们先看下SOFABoot到底装配了哪些bean,下面列了一些核心的bean。

@Configuration
public class SofaBootHealthCheckAutoConfiguration { @Bean
public ReadinessCheckListener readinessCheckListener() {
return new ReadinessCheckListener();
} @Bean
public HealthCheckerProcessor healthCheckerProcessor() {
return new HealthCheckerProcessor();
} @Bean
public HealthIndicatorProcessor healthIndicatorProcessor() {
return new HealthIndicatorProcessor();
} @Bean
public AfterReadinessCheckCallbackProcessor afterReadinessCheckCallbackProcessor() {
return new AfterReadinessCheckCallbackProcessor();
}
}

上面一共罗列了4个bean,一个是应用监听器ReadinessCheckListener,这个是入口逻辑,下文核心逻辑讲解将从这个类开始。

一个是Readiness检查完毕的后置处理器AfterReadinessCheckCallbackProcessor,这个职责也比较容易理解,当Readiness完成之后,就会执行去处理逻辑。

另外两个处理器则是健康检查的处理器,HealthCheckerProcessor是针对SOFABoot提供的HealthChecker类型的bean进行处理,HealthIndicatorProcesso是针对SpringBoot提供的HealthIndicator类型的bean进行处理。

2.2 核心逻辑解析

ReadinessCheckListener

这个监听器实现了ApplicationListener,并监听ContextRefreshedEvent事件,当应用上下文刷新完成后,触发监听器收到该事件,执行下面的逻辑。

// ReadinessCheckListener 接收到刷新事件后的执行逻辑
public void onApplicationEvent(ContextRefreshedEvent event) {
if (applicationContext.equals(event.getApplicationContext())) {
healthCheckerProcessor.init();
healthIndicatorProcessor.init();
afterReadinessCheckCallbackProcessor.init();
readinessHealthCheck();
readinessCheckFinish = true;
}
}

接收到上下文的刷新事件后,主要做了四件事情,前面三件是为最后一件事情做准备的:

  • 健康检查处理器初始化,将上下文中所有HealthChecker类型的bean都放在map中,等待Readiness检查。
  • 健康指标处理器初始化,将上下文中所有ReactiveHealthIndicator类型的bean都放在map中,等待Readiness检查。
  • Readiness检查后置处理器初始化,将上下文中所有的ReadinessCheckCallback类型的bean都放在map中,等待Readiness检查完毕后调用。
  • Readiness健康检查,前面三步已经准备好了HealthChecker、ReactiveHealthIndicator和ReadinessCheckCallback的所有bean,这一步是真正开始Readiness健康检查。Readiness检查核心逻辑如下:
public void readinessHealthCheck() {
if (skipAllCheck()) {
logger.warn("Skip all readiness health check.");
} else {
if (skipComponent()) {
logger.warn("Skip HealthChecker health check.");
} else {
healthCheckerStatus = healthCheckerProcessor
.readinessHealthCheck(healthCheckerDetails);
}
if (skipIndicator()) {
logger.warn("Skip HealthIndicator health check.");
} else {
healthIndicatorStatus = healthIndicatorProcessor
.readinessHealthCheck(healthIndicatorDetails);
}
}
healthCallbackStatus = afterReadinessCheckCallbackProcessor
.afterReadinessCheckCallback(healthCallbackDetails);
if (healthCheckerStatus && healthIndicatorStatus && healthCallbackStatus) {
logger.info("Readiness check result: success");
} else {
logger.error("Readiness check result: fail");
}
}

从上面的逻辑,我们可以看到HealthChecker和HealthIndicator的处理都是可以基于配置跳过的,不是必须执行的。当HealthChecker、HealthIndicator、ReadinessCheckCallback对应的处理器都执行成功之后,打印相应的结果信息。

  • healthCheckerProcessor的readinessHealthCheck主要是去收集每一个HealthChecker的检查结果,当所有HealthChecker的检查结果都为true时,返回true。这个过程持续时间比较长,如果一个HealtchChecker返回的结果是false,processor会定时重试再去获取其结果,直到其返回true或者重试到最大次数。
  • healthIndicatorProcessor的readinessHealthCheck逻辑和healthCheckerProcessor的类似,去收集每一个HealthIndicator的指标的具体信息,但持续过程比较短,无需重试,执行完成则返回true。
  • afterReadinessCheckCallbackProcessor在Readiness检查完毕之后,逐一去调用所有ReadinessCheckCallback类型的bean,执行readiness的后置处理。

核心逻辑,其实就是三个不同类型Bean的处理器,去遍历执行各自的bean集合,收集执行结果。

  • HealthChecker类型:这个是SOFABoot提供的,其处理器是SOFABoot提供的,处理器去遍历执行时需要重试获取检查结果。
  • ReactiveHealthIndicator类型:这个是SpringBoot本身提供的,其处理器是SOFABoot提供的,用于处理SpringBoot本身的健康检查,处理器遍历执行时无需重试获取检查结果。
  • ReadinessCheckCallback类型:这个是SOFABoot提供的,其处理器是SOFABoot提供的,Readiness检查后的后置处理bean。

可以看出,SOFABoot提供的HealthChecker和SpringBoot提供的HealthIndicator职责有点类似,但是存在差异性,HealthChecker中是适合于Readiness的,其实现类指明重试次数retryCount和重试间隔retryTimeInterval,在应用刚启动时候,做Readiness检查不一定能一次性成功,那么就需要这种最大重试机制,所以HealthChecker的处理器在readinessHealthCheck过程中持续的时间会更长。

public interface HealthChecker {
// some method..
default int getRetryCount() {
return 0;
}
default long getRetryTimeInterval(){
return 0;
}
// some method..
}

而SpringBoot本身提供的是Liveness机制,所以HealthIndicator在运行期间,本身就是一直定时去获取的,没有最大重试次数,只要一直在运行,就要定时去检查。

public interface HealthIndicator {
// Return an indication of health.
Health health();
}

当然,SOFABoot在重试完成了HealthCheck的健康检查之后,再完成了一遍HealthIndicator的健康检查,且执行了一遍后置逻辑,都成功之后,检查结果才是健康的,才可以正式对外提供服务。

显然,要扩展自己的检查指标也是很容易的,如果是要Readiness Check的,则实现一个HealthCheck类,如果是需要Liveness Check的,则实现一个HealthIndicator即可。

四 结语

本文开篇介绍了SOFABoot和SpringBoot的关系,在SpringBoot的健康检查中,提供了Liveness Check能力,SOFABoot在此之上新增了Readiness Check能力。通过starter的配置一步一步找到其入口逻辑,并对应用监听器、HealthCheck、HealthIndicator和ReadinessCheckCallback对应的三个处理的逻辑进行了核心解读,并说明了HealthCheck和HealthIndicator的区别。


近期文章:

蚂蚁SOFA系列(1) - 聊聊SOFA的模块化

蚂蚁SOFA系列(2) - SOFABoot的Readiness健康检查机制的更多相关文章

  1. Knative Serving 健康检查机制分析

    作者|  阿里云智能事业群技术专家牛秋霖(冬岛) 导读:从头开发一个Serverless引擎并不是一件容易的事情,今天咱们就从Knative的健康检查说起.通过健康检查这一个点来看看Serverles ...

  2. 为你的docker容器增加一个健康检查机制

    1.健康检查 在分布式系统中,经常需要利用健康检查机制来检查服务的可用性,防止其他服务调用时出现异常.自 1.12 版本之后,Docker 引入了原生的健康检查实现. 如何给Docke配置原生健康检查 ...

  3. Spring Cloud Alibaba Nacos 的 2 种健康检查机制!

    Spring Cloud Alibaba Nacos 作为注册中心不止提供了服务注册和服务发现功能,它还提供了服务可用性监测的机制.有了此机制之后,Nacos 才能感知服务的健康状态,从而为服务调用者 ...

  4. Eureka心跳健康检查机制和Spring boot admin 节点状态一直为DOWN的排查(忽略某一个节点的健康检查)

    https://www.jdon.com/springcloud/eureka-health-monitoring.html 运行阶段执行健康检查的目的是为了从Eureka服务器注册表中识别并删除不可 ...

  5. 蚂蚁SOFA系列(1) - 聊聊SOFA的模块化

    作者:404,转载请注明出处.欢迎关注公众号:404P. SOFA是蚂蚁自研的一套金融级分布式中间件,目前正在逐步向业界开源.SOFA的全称有两个,最早是Service Oriented Fabric ...

  6. Docker系列(五):.Net Core实现k8s健康探测机制

    k8s通过liveness来探测微服务的存活性,判断什么时候该重启容器实现自愈.比如访问 Web 服务器时显示 500 内部错误,可能是系统超载,也可能是资源死锁,此时 httpd 进程并没有异常退出 ...

  7. aspnetcore.webapi实践k8s健康探测机制 - kubernetes

    1.浅析k8s两种健康检查机制 Liveness k8s通过liveness来探测微服务的存活性,判断什么时候该重启容器实现自愈.比如访问 Web 服务器时显示 500 内部错误,可能是系统超载,也可 ...

  8. aspnetcore.webapi实战k8s健康探测机制 - kubernetes

    1.浅析k8s两种健康检查机制 Liveness k8s通过liveness来探测微服务的存活性,判断什么时候该重启容器实现自愈.比如访问 Web 服务器时显示 500 内部错误,可能是系统超载,也可 ...

  9. 9-lvs-lvs集群-及keepalived健康检查

    注意: 配置前需要将上一篇的配置都清除掉 ifconfig eth1: down service ipvsadm restart nginx作为请求分发服务器时, 有健康检查机制, 挂了的服务器不会在 ...

随机推荐

  1. kali Metasploit 连接 Postgresql 默认密码

    使用 metasploit 时, 1. 启动 postgresql service postgresql start 2. 自行测试 postgresql 是否安装成功 根据需要,自行 修改 post ...

  2. Docker最简单入门之(一)——介绍和配置Docker

    0. 前言 最近学完了Dokcer,特别记录一下,算是对自己学习成果的一个总结.以便自己能够更好的理解Docker.粗略估计了一下,我大概会分成4个部分,只记录一下常用的操作,至于一些比较难的操作或者 ...

  3. java8(1)--- lambda

    项目马上切java8了,之前对于java8的东西都是东打一棒西打一锤的了解了些.这次搜集整理了下,从lambda到stream相关的API等. 1.Lambda和匿名内部类 Lambda 是一个匿名的 ...

  4. 守望先锋app(2)

    上次的功能完成了英雄名字.id.头像的下载并使用RecyclerView展示, 所以接下来就是点击每个英雄的caraview就能打开下一个活动进行英雄的介绍.先打开暴雪的官网查看有那些技能.故事.图片 ...

  5. 洛谷 P4124 [CQOI2016]手机号码

    题意简述 求l~r之间不含前导零,至少有三个相邻的相同数字,不同时含有4和8的11位正整数的个数 题解思路 数位DP,注意在l,r位数不够时补至11位 代码 #include <cstdio&g ...

  6. EFCore + MySql codeFirst 迁移 Migration出现的问题

    第二次使用Migration update-database的时候出现以下错误: System.NotImplementedException: The method or operation is ...

  7. 前端面试题集锦(二)之CSS部分

    1.CSS中的选择器都有哪些?权限情况如何? 解答: (1)类选择器 .className  (2) ID选择器 #id  (3) 元素选择器 div 可以多个,以逗号隔开 (4)父子选择器 以空格隔 ...

  8. vscode 支持 threejs 的智能提示

    VSCode Typings and Intellisense: Dummy Learning VS-Code 1 Jun 20, 2016 Updated on Jun 20 2016 for 1. ...

  9. .netcore持续集成测试篇之 .net core 2.1项目集成测试

    系列目录 从.net到.net core以后,微软非常努力,以每年一到两个大版本的频率在演进.net core,去年相继发布了.net core 2.1和2.2,其中2.1是长期支持版,不断的快速更新 ...

  10. ubuntu-18.10 虚拟机 配置网络环境

    查询主机系统ip 使用virtualbox 设置网络模式为桥接模式 设置静态 ip 与网关 关闭防火墙 sudo ufw disable