springboot优雅shutdown时如何保障异步线程的安全

我前面写了一篇springboot优雅shutdown的文章,看起来一切很美好。
https://blog.csdn.net/chenshm/article/details/139640775
那是因为没有进行多线程测试。如果一个请求中包括阻塞线程(主线程)和非阻塞线程(异步线程),会是什么效果?接下来我们就测试一番。

1. 验证优雅shutdown的异步线程安全性

  • 确认graceful shutdown配置

graceful shutdown config
查看源码可以看到springboot graceful shutdown默认只会等待30s,我这里设置更长的时间只是方便测试,实际设置还是需要根据你业务api最长执行时间来配置。

graceful shutdown default timeout setting

  • 准备测试代码
/*** @Author 公众号: IT三明治* @Date 2024/6/15* @Description:*/
@Slf4j
@RestController
@RequestMapping("/api")
public class DemoController {@GetMapping("/{userId}")public ResultVo<Object> getUserInfo(@PathVariable String userId) throws InterruptedException {log.info("userId:{}", userId);Runnable runnable = () -> {for (int i = 0; i < 60; i++) {log.info("async thread to update user login info to other services, service num: {}", i);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}};Thread thread = new Thread(runnable);thread.start();for (int i = 0; i < 30; i++) {log.info("querying user info for {}, waiting times: {}", userId, i);Thread.sleep(1000);}return ResultVo.ok();}
}

这里我设置非阻塞线程的循环是60次,大概60s完成,阻塞线程循环只有30次,大概30s完成。主要是为了测试我的阻塞线程完成后,graceful shutdown能不能保证我的异步线程安全。

  • 请求api
Administrator@USER-20230930SH MINGW64 /d/git/micro-service-logs-tracing
$ curl http://localhost:8080/api/sandwich
  • shutdown app(Ctrl+F2)
  • 查看日志
    shutdown日志

可以看到shutdown信号发出之后,两个线程都还在跑,但是阻塞线程(0-29)结束之后,异步线程也跟着终结了。它的循环应该是从0到59才算结束,但是只跑到30,所以异步线程是不安全的。

  • 验证主线程返回结果
    阻塞线程还是安全的,response正常返回了。
    api response correctly but the async thread not yet finished

其实这种测试方法并不局限于解决springboot的问题,其他微服务也是类似的。过去我看到一些朋友测试release的安全性,只是不断call health api,只要release 期间health api没有返回异常就当作ok了,其实这只能验证你的负载均衡服务的可靠性,你自己app的安全问题还是没有得到解决。
既然问题找到了,接下来我来解决它。

2. 确保优雅shutdown app时异步线程也安全

2.1 优化代码

前面的异步线程只是简单地写个野线程,并不规范,我先优化一下。

  • 把野线程放到线程池执行;
  • 利用mbean的PreDestroy来在servcie销毁前先等待异步线程完成;
  • 利用ExecutorService 的awaitTermination方法预判断异步线程的最长等待时间,等待异步线程完成,如果线程没有按时完成再强制结束。
/*** @Author 公众号: IT三明治* @Date 2024/6/15* @Description:*/
@Slf4j
@Service
public class AsyncServiceImpl implements AsyncService {private final ExecutorService executorService;public AsyncServiceImpl() {this.executorService = Executors.newFixedThreadPool(10);}@Overridepublic void feedUserInfoToOtherServices(String userId) {executorService.execute(() -> {for (int i = 0; i < 35; i++) {log.info("async thread to update {} login info to other services, service num: {}", userId, i+1);try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});}@PreDestroypublic void tearDown() {if (null != executorService) {executorService.shutdown();try {if (!executorService.awaitTermination(50, TimeUnit.SECONDS)) {executorService.shutdownNow();}} catch (InterruptedException e) {log.info("PreDestroy executorService is interrupted", e);executorService.shutdownNow();}}}
}

api代码调整如下

/*** @Author 公众号: IT三明治* @Date 2024/6/15* @Description:*/
@Slf4j
@RestController
@RequestMapping("/api")
public class DemoController {@ResourceAsyncService asyncService;@GetMapping("/{userId}")public ResultVo<Object> getUserInfo(@PathVariable String userId) throws InterruptedException {log.info("userId:{}", userId);asyncService.feedUserInfoToOtherServices(userId);for (int i = 0; i < 30; i++) {log.info("updating user info for {}, waiting times: {}", userId, i+1);Thread.sleep(1000);}return ResultVo.ok();}
}

2.2 验证shutdown过程异步线程的安全

从新代码看来,我们期待的结果是一个api请求,主线程循环从1到30,异步线程是从1到35,主线程先完成,异步线程会在AsyncServiceImpl servcie bean销毁前先等待异步线程完成。接下来是验证步骤。

  • 重启服务
  • call api
Administrator@USER-20230930SH MINGW64 /d/git/micro-service-logs-tracing
$ curl http://localhost:8080/api/sandwich
  • shutdown app(Ctrl + F2)
  • 查看日志
    异步线程安全退出日志

分析日志发现一切如代码所料,app graceful shutdown的时候,异步线程的安全性得到保障。
这个过程看起来非常完美,其实还不够完美,解决方案没最好,只有更好。请先关注我,容我研究一下,下期告诉你为什么。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/29477.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux安装MySQL以及远程连接

1、Linux安装MySQL 1.1、准备解压包 MySQL5.x解压包 提取码&#xff1a;9y7n 1.2、通过rpm脚本安装 切记安装顺序&#xff1a;common --> libs --> client --> server 因为它们之间存在依赖关系&#xff0c;所以务必按照顺序安装 安装前请确保当前目录/文…

【差分数组】2772. 使数组中的所有元素都等于零

本文涉及知识点 差分数组 LeetCode2772. 使数组中的所有元素都等于零 给你一个下标从 0 开始的整数数组 nums 和一个正整数 k 。 你可以对数组执行下述操作 任意次 &#xff1a; 从数组中选出长度为 k 的 任一 子数组&#xff0c;并将子数组中每个元素都 减去 1 。 如果你可…

【Android】安Android Studio环境搭建注意点

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

PowerBi 获取指定时间间隔的日期的方法

获取指定时间间隔的日期&#xff0c;比如我们想得到2024年5月31日后的第三天。 网络上的教程一般是使用DATEADD()函数。 但是这个函数返回的是表。假如我们的需求是不做汇总等计算&#xff0c;只是把它作为一个计算列&#xff0c;或者度量值&#xff0c;那么我更推荐用DATE(&…

信息系统项目管理师 | 新一代信息技术

关注WX&#xff1a;CodingTechWork 物联网 定义 The Internet of Things是指通过信息传感设备&#xff0c;按约定的协议&#xff0c;将任何物品与互联网连接&#xff0c;进行信息交互和通信&#xff0c;以实现智能化识别。定位、跟踪、监控和管理的一种网络。物联网主要解决…

Part 4.4 树形动态规划

树形动态规划&#xff0c;即在树上进行的动态规划。 因为树的递归性质&#xff0c;树形动态规划一般都是递归求解的。 没有上司的舞会 题目描述 某大学有 n n n 个职员&#xff0c;编号为 1 … n 1\ldots n 1…n。 他们之间有从属关系&#xff0c;也就是说他们的关系就像…

禅道系统忘记密码-直接更改数据库数据解决

禅道系统很久不用密码忘记了&#xff0c;这里采用直接进数据库修改密码方式解决。 登录到系统&#xff0c;进入禅道安装目录&#xff0c;这里安装在/opt/zbox 基本思路如下&#xff1a; 1.找到数据库账号和密码。 2.使用mysql命令登陆数据库。 3.在禅道数据库对应用户表内更…

鸿蒙原生应用元服务开发-位置服务申请权限

申请位置权限开发指导 场景概述 应用在使用位置服务系统能力前&#xff0c;需要检查是否已经获取用户授权访问设备位置信息。如未获得授权&#xff0c;可以向用户申请需要的位置权限。 系统提供的定位权限有&#xff1a; ohos.permission.LOCATION&#xff1a;用于获取精准位置…

keepalived服务详解与实验 基于centos8

目录 keepalivedHA简介常用的高可用软件keepalived简介 keepalived常用模块keepalived功能简介keepalived常用文件keepalived配置文件详解keepalived实验1-上手环境准备安装服务主配置文件修改启动服务效果查看 keepalived脑裂1. 脑裂现象简介2. 脑裂的原因3. 脑裂的预防和解决…

电商API接口是什么意思?有什么作用?

电商API接口是电子商务领域中一种技术解决方案&#xff0c;它允许不同的软件系统之间进行交互和数据交换。 在电商场景下&#xff0c;电商API接口可以实现的功能非常丰富&#xff0c;例如&#xff1a; 商品管理&#xff1a;获取商品列表、商品详情、搜索商品、上下架商品等&a…

一文详解:什么是小程序SDK?

什么是小程序SDK&#xff1f; 首先来看看概念&#xff1a;小程序SDK&#xff08;Software Development Kit&#xff09;是用于开发和扩展小程序的工具集合。可以理解为一套工具箱&#xff0c;专门帮助开发者建立和定制小程序应用程序。这些工具包括了开发小程序所需的各种代码…

【网络编程】多进程服务器端

并发服务器的实现 多进程服务器:通过创建多个进程提供服务多路复用服务器:通过捆绑并统一管理IO对象提供服务。多线程服务器:通过生成与客户端等量的线程提供服务。、 理解进程process 定义&#xff1a;占用内存空间的正在运行的程序。 CPU核和进程数&#xff1a;1个CPU 中…

wegame启动游戏错误代码126,加载x3daudio1_7.dll失败怎么解决

x3daudio1_7.dll是一个重要的动态链接库文件&#xff0c;属于Microsoft DirectX SDK的一部分&#xff0c;主要服务于音频处理领域&#xff0c;特别是在游戏和多媒体应用程序中提供高级的3D音频效果。 基本属性与功能 文件名称&#xff1a;x3daudio1_7.dll 类型&#xff1a;动…

YOLO-World:开启实时开放词汇目标检测的新篇章

目标检测作为计算机视觉领域的基石之一&#xff0c;其发展一直备受学术界和工业界的关注。传统的目标检测方法通常受限于固定词汇表的约束&#xff0c;即只能在预定义的类别集合中进行检测。然而&#xff0c;现实世界中的对象种类繁多&#xff0c;远远超出了任何固定词汇表的覆…

MySQl基础入门⑯【操作视图】完结

上一边文章内容 表准备 CREATE TABLE Students (id INT AUTO_INCREMENT PRIMARY KEY,name VARCHAR(100),email VARCHAR(255),major VARCHAR(100),score int,phone_number VARCHAR(20),entry_year INT,salary DECIMAL(10, 2) );数据准备 INSERT INTO Students (id, name, ema…

windows修改hosts文件、windows刷新dns缓存

文章目录 一、windows修改hosts文件 一、windows修改hosts文件 &#xff08;1&#xff09;定位hosts文件&#xff1a;首先&#xff0c;需要找到hosts文件的位置。它通常位于C:\Windows\System32\drivers\etc\目录下。 &#xff08;2&#xff09;以管理员身份运行记事本&#x…

Python 库PySpark,一个超级强大的数据处理引擎

目录 01初识 PySpark 为什么选择 PySpark? 安装 PySpark 配置 PySpark 02基本操作 创建 RDD 基本 RDD 操作 03DataFrame 和 Spark SQL 创建 DataFrame 基本 DataFrame 操作 使用 Spark SQL 04机器学习与流处理 …

UltraEdit电脑版下载_UltraEdit文本编辑器中文版下载_UltraEdit 2024最新版软件安装包下载附加详细安装步骤

UltraEdit中文版是一款功能强大的文本编辑器&#xff0c;几乎可以满足你所有的工作需求。使用UltraEdit文本编辑器可以操作更多记事本所不能处理的工作。如&#xff1a;基本的编辑文本、十六进制、ASCLL码、语法加亮、代码折叠、代码单词拼写检查等、C 及 VB 指令突显等,附有 H…

mongo工具篇 --- mongostats

一、使用 1、安装 安装链接 2、命令参数 -h&#xff0c;–host hostname&#xff1a;指定MongoDB主机名和端口&#xff0c;例如&#xff1a;-h localhost:12017&#xff0c;默认-a&#xff0c;–authenticationMechanism mechanism&#xff1a;指定认证机制-u&#xff0c…

分享三个仓库

Hello , 我是恒。大概有半个月没有发文章了&#xff0c;都写在文档里了 今天分享三个我开源的项目&#xff0c;比较小巧但是有用 主页 文档导航 Github地址: https://github.com/lmliheng/document 在线访问:http://document.liheng.work/ 里面有各种作者书写的文档&#xff…