什么是存活就绪检查?
存活检查: 检查一个服务是否存活, 如果不存活,需要启动新的服务进行补充替换。对于JAVA 服务,如在Java 进程异常退出、 Java 进程假死等情况等不可自动恢复的情况,就任务是不存活的。
就绪检查: 检查一个服务是否准备好,对外提供服务。 主要应用在新应用启动后,不立刻提供对外服务,而需要通过就绪检查确定提供的服务的资源都准备好后才提供对外的服务。
为什么需要存活、就绪检查?
对于一个被线上使用的服务来说,降低服务的可用性的原因(不考虑编码层面问题)主要有:
- 发布不平滑。 发布时不能平滑替换服务,导致部分请求报错,降低可用性。
- 服务故障修复不及时。 一般是服务过载情况下, 服务缺失故障自动修复能力,或自动扩展能力不足,导致服务不可用。
- 没有备份。 单机或单区运行,在服务器异常或区域性依赖异常时,导致服务不可用。
就绪检查主要会用在保障发布时保障服务可用时再提供对外服务使用,是平滑发布保障的关键一环。
存活检查主要用在服务过程中,服务发生不可恢复故障时,发布工具能够自动及时的进行补救。
如何实现存活就绪检查?
根据存活就绪检查目的, 实现方法一般会对应用依赖重要组件进行检查,汇总后提供检查结果输出。 可以看出来这是比较适合进行封装的动作,SpringBoot Actuator 就这样一个包,虽然存活就绪只是其功能之一。如果需要自己实现,SpringBoot Actuator 是一个很好的参考。
SpringBoot Actuator 做为存活就绪检查步骤
SpringBoot Actuator提供的健康检查端点 /actuator/health ,虽然也可以做为存活和就绪检查使用,通过对存活和就绪作用及原理的了解,知道其满足不了存活就绪分离检测。 不过在Spring Boot 2.3及以上版本,Actuator新增了分组功能,通过对健康端点的各组件进行分组,通过分组自定义组内包含的检测组件,实现不同作用的检测。如可以设置liveness和readiness 两个分组, 然后通过/actuator/health/liveness和/actuator/health/readiness来作为检测接口。
使用SpringBoot Actuator 基本上包含大部分组件的检查端点实现, 通过配置可以灵活的配置需要使用的端点。配置示例如下:
management:
endpoints:
web:
exposure:
include: "health,info,env" #开放访问的接口
base-path: /actuator #自定义基础路径, 默认/actuator
endpoint:
health:
enabled: true #是否开启,默认true
show-details: ALWAYS #health 接口是否返回详情信息,方便排查一般返回
group:
readiness: #就绪检查组,访问地址 /health/readiness
include: "ping" #就绪组检查的组件
liveness: #存活检查组,访问地址 /health/liveness
include: "ping" #存活组要检查的组件
health:
defaults.enabled: false #关闭自动启用组件检查,改由手动启用
ping.enabled: true #defaults.enabled 为false 情况下, 手动指定开启的检查端点
mysql.enabled: true
如何确定哪些组件需要加入存活或就绪检测
一般项目不复杂,引入组件很少或没有重要影响系统运行的三方组件情况下,可以使用
Spring Boot Actuator提供的默认设置即可满足需求。但因为默认设置会默认加载所有引入组件包中带的Actuator 健康监测端点,而不管这个端点检测的组件是否使用,是否重要。为防止一些无效的包或不重要的组件检测故障导致存活或就绪检查失败,推荐自定义配置来定义设置线上项目的health 端点, 即把defaults.enabled 设置为false, 然后手工开启需要进行检测的组件。
具体项目中包含哪些可用端点呢, 可以通过设置defaults.enabled 设置为true,
开启actuator 健康检查,将health.defaults.enabled 设置为true的情况下,访问/actuator/health 就能查看到当前项目所有检查的端点。
如果项目没有需要检测的组件,或组件不适合作为就绪或存活检查项, 可以启用ping 组件做空白验证。对于有重要组件,但没有自带的检测端点,也可以自实现端点接口HealthIndicator。 示例:
public class MemHealthIndicator implements HealthIndicator {
private final Logger LOGGER = LoggerFactory.getLogger(MemHealthIndicator.class);
@Override
public Health health() {
MemoryMXBean memoryMbean = ManagementFactory.getMemoryMXBean();
MemoryUsage usage = memoryMbean.getHeapMemoryUsage();
Long max = usage.getMax();
Long used = usage.getUsed();
double useRate = used.doubleValue()/max.doubleValue();
String useInfo = toString(usage);
if(useRate > 0.9){
LOGGER.info("jvm 内存使用超过90%,使用率{}",useInfo);
return Health.down().withDetail("内存使用率",usage).build();
}
LOGGER.info("jvm 内存使用{}",useInfo);
return Health.up().withDetail("内存使用率",usage).build();
}
}
//其他配置类中
...
@Bean
@ConditionalOnEnabledHealthIndicator("mem") // 定义端点名称,配置中可以使用mem.enable:true 启用
public HealthIndicator memHealthIndicator(){
return new MemHealthIndicator();
}
....