使用SpringBoot钩子或Actuator实现优雅停机
wptr33 2025-06-30 20:45 19 浏览
服务如何响应停机信号
在java中我们可以直接利用通过Runtime来注册一个停机的钩子函数
Runtime.getRuntime().addShutdownHook(new Thread(
new Runnable() {
@Override
public void run() {
// 实现自己的停机逻辑,例如:关闭资源、等待请求处理完成等....
System.out.println("我被kill了");
}
}
));
在SpringBoot环境下,默认已经给我们注册了这个钩子:
org.springframework.boot.SpringApplication#refreshContext
在这个钩子函数中,Spring容器会去执行自己的doClose方法关闭容器
跟踪这个doClose方法会发现,关闭容器的第一步会发送一个ContextClosedEvent事件
因此在SpringBoot环境下我们只需要实现一个事件监听器就可以捕捉到应用的停机信号,实现优雅停机
@Component
public class MyShutdownHook implements ApplicationListener<ContextClosedEvent> {
@Override
public void onApplicationEvent(ContextClosedEvent event) {
// 优雅停机。。。
}
}
利用SpringBootActuator实现探针
在上篇文章中,咱们提到了需要配置K8s的探针,配置如下:
apiVersion: apps/v1
kind: Deployment
spec:
template:
spec:
containers:
# 在containers下添加两个指针
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
# pod启动后延迟60s探测
initialDelaySeconds: 45
# 每30s测一次
periodSeconds: 10
# 每次探测最大超时时间3s
timeoutSeconds: 3
# 连续6次探测失败则重启pod
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 45
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
其中需要实现两个接口:
- /actuator/health/liveness,用于存活探测
- /actuator/health/liveness,用于就绪探测
如何实现这两个接口呢?
我们只需要接入spring boot actuator,再进行一些简单配置即可
第一步:引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
第二步:添加配置
endpoints:
web:
exposure:
include: health
endpoint:
health:
group:
liveness:
include:
- ping
readiness:
include:
- "*"
show-details: always
endpoints.web.exposure.include代表服务需要暴露哪些actuator的端点,咱们这里只讨论健康检查,因此只打开health端点
在上面的配置中通过endpoint.health.group将health端点分为两组:liveness、readiness,对应咱们在探针中配置的livenessProbe跟readinessProbe
在liveness探测中,我只配置了ping,其作用相当于访问下面这样的一个接口:
@RestController("/health")
public class HealthCheck {
@GetMapping
public String check(){
return "ok";
}
}
对于readiness探测,配置了*,代表要对所有的组件进行检查
我们可以通过访问:localhost:8080/actuator/health/readiness来确认目前检查了哪些组件,其返回格式如下:
{
"status": "DOWN",
"components": {
"custom": {
"status": "DOWN",
"details": {
"message": "My application is healthy!"
}
},
"db": {
"status": "UP",
"details": {
"database": "MySQL",
"validationQuery": "isValid()"
}
},
"mongo": {
"status": "DOWN",
"details": {
"error": "org.springframework.data.mongodb.UncategorizedMongoDbException: Exception authenticating MongoCredential{mechanism=SCRAM-SHA-1}; nested exception is com.mongodb.MongoSecurityException: Exception authenticating MongoCredential"
}
},
"ping": {
"status": "UP"
}
}
}
通过这个接口的访问信息,咱们也可以发现目前我这个服务的mongoDB是无法连接的,另外有一个定制的端点custom是挂掉的。需要注意的是,只要有一个端点检查是down的状态,整个检查就是不通过的。
定制一个检查的端点也非常简单,如下:
// 需要在endpoint.health.group.readiness.include中添加这个custom端点
@Component("custom")
public class CustomHealthCheck extends AbstractHealthIndicator {
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
if (检查不通过) {
builder.down().withDetail("reason", "我挂掉了").withException(new RuntimeException("测试异常"));
} else {
builder.up();
}
}
}
踩过的坑
- 添加liveness跟readiness探针后, healthcheck会根据配置判断组件的状态, 如果组件状态异常,会导致pod重启, 在特殊场景下, 组件异常并不需要重启, 需要使用方自行判断。可以通过http://localhost:8080/actuator/health/readiness跟http://localhost:8080/actuator/health/liveness确认当前应用配置了哪些组件举例场景:redis挂掉, 但是在服务中, redis缓存允许穿透或者异常, redis挂掉不应该重启机器, 这个在healthcheck中,redis状态异常不应该触发liveness和readness失败
- initialDelaySeconds的值应该根据应用启动时间进行合理设置,如果设置过短,会导致pod反复被kill无法正常启动
- 整个优雅启停的上线应该分为两步
- 代码改造,接入SpringBootActuator
- deployment文件更新
- 推荐的做法是,代码改造完成后先上线,上线之后选择某一个pod测试服务的liveness跟readiness探测接口,确认探测接口的调用对服务没有影响后再apply改动过的deployment文件。笔者在实际落地的时候就碰到过某些测试环境没问题,上线之后只要调用redis的探测,服务立马宕机,之后就进入不断重启、宕机的死循环,并且,回滚的时候需要同时操作deployment文件跟回滚代码,那种感觉别提有多酸爽
相关推荐
- redis的八种使用场景
-
前言:redis是我们工作开发中,经常要打交道的,下面对redis的使用场景做总结介绍也是对redis举报的功能做梳理。缓存Redis最常见的用途是作为缓存,用于加速应用程序的响应速度。...
- 基于Redis的3种分布式ID生成策略
-
在分布式系统设计中,全局唯一ID是一个基础而关键的组件。随着业务规模扩大和系统架构向微服务演进,传统的单机自增ID已无法满足需求。高并发、高可用的分布式ID生成方案成为构建可靠分布式系统的必要条件。R...
- 基于OpenWrt系统路由器的模式切换与网页设计
-
摘要:目前商用WiFi路由器已应用到多个领域,商家通过给用户提供一个稳定免费WiFi热点达到吸引客户、提升服务的目标。传统路由器自带的Luci界面提供了工厂模式的Web界面,用户可通过该界面配置路...
- 这篇文章教你看明白 nginx-ingress 控制器
-
主机nginx一般nginx做主机反向代理(网关)有以下配置...
- 如何用redis实现注册中心
-
一句话总结使用Redis实现注册中心:服务注册...
- 爱可可老师24小时热门分享(2020.5.10)
-
No1.看自己以前写的代码是种什么体验?No2.DooM-chip!国外网友SylvainLefebvre自制的无CPU、无操作码、无指令计数器...No3.我认为CS学位可以更好,如...
- Apportable:拯救程序员,IOS一秒变安卓
-
摘要:还在为了跨平台使用cocos2d-x吗,拯救objc程序员的奇葩来了,ApportableSDK:FreeAndroidsupportforcocos2d-iPhone。App...
- JAVA实现超买超卖方案汇总,那个最适合你,一篇文章彻底讲透
-
以下是几种Java实现超买超卖问题的核心解决方案及代码示例,针对高并发场景下的库存扣减问题:方案一:Redis原子操作+Lua脚本(推荐)//使用Redis+Lua保证原子性publicbo...
- 3月26日更新 快速施法自动施法可独立设置
-
2016年3月26日DOTA2有一个79.6MB的更新主要是针对自动施法和快速施法的调整本来内容不多不少朋友都有自动施法和快速施法的困扰英文更新日志一些视觉BUG修复就不翻译了主要翻译自动施...
- Redis 是如何提供服务的
-
在刚刚接触Redis的时候,最想要知道的是一个’setnameJhon’命令到达Redis服务器的时候,它是如何返回’OK’的?里面命令处理的流程如何,具体细节怎么样?你一定有问过自己...
- lua _G、_VERSION使用
-
到这里我们已经把lua基础库中的函数介绍完了,除了函数外基础库中还有两个常量,一个是_G,另一个是_VERSION。_G是基础库本身,指向自己,这个变量很有意思,可以无限引用自己,最后得到的还是自己,...
- China's top diplomat to chair third China-Pacific Island countries foreign ministers' meeting
-
BEIJING,May21(Xinhua)--ChineseForeignMinisterWangYi,alsoamemberofthePoliticalBureau...
- 移动工作交流工具Lua推出Insights数据分析产品
-
Lua是一个适用于各种职业人士的移动交流平台,它在今天推出了一项叫做Insights的全新功能。Insights是一个数据平台,客户可以在上面实时看到员工之间的交流情况,并分析这些情况对公司发展的影响...
- Redis 7新武器:用Redis Stack实现向量搜索的极限压测
-
当传统关系型数据库还在为向量相似度搜索的性能挣扎时,Redis7的RedisStack...
- Nginx/OpenResty详解,Nginx Lua编程,重定向与内部子请求
-
重定向与内部子请求Nginx的rewrite指令不仅可以在Nginx内部的server、location之间进行跳转,还可以进行外部链接的重定向。通过ngx_lua模块的Lua函数除了能实现Nginx...
- 一周热门
-
-
C# 13 和 .NET 9 全知道 :13 使用 ASP.NET Core 构建网站 (1)
-
因果推断Matching方式实现代码 因果推断模型
-
git pull命令使用实例 git pull--rebase
-
git pull 和git fetch 命令分别有什么作用?二者有什么区别?
-
面试官:git pull是哪两个指令的组合?
-
git 执行pull错误如何撤销 git pull fail
-
git fetch 和git pull 的异同 git中fetch和pull的区别
-
git pull 之后本地代码被覆盖 解决方案
-
还可以这样玩?Git基本原理及各种骚操作,涨知识了
-
git命令之pull git.pull
-
- 最近发表
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)