ToB企服应用市场:ToB评测及商务社交产业平台

标题: springboot优雅shutdown时异步线程安全优化 [打印本页]

作者: 冬雨财经    时间: 2024-7-17 11:39
标题: springboot优雅shutdown时异步线程安全优化
前面针对graceful shutdown写了两篇文章
第一篇:
https://blog.csdn.net/chenshm/article/details/139640775
只思量了阻塞线程,没有思量异步线程
第二篇:
https://blog.csdn.net/chenshm/article/details/139702105
第二篇思量了多线程的安全性,包括异步线程。
1. 为什么还需要优化呢?

因为第二篇的写法还不够优美,它存在以下缺陷。

2. 代码优化


  1. package com.it.sandwich.service.impl;
  2. import com.it.sandwich.service.Demo2Service;
  3. import lombok.extern.slf4j.Slf4j;
  4. import org.springframework.scheduling.annotation.Async;
  5. import org.springframework.stereotype.Component;
  6. import org.springframework.stereotype.Service;
  7. /**
  8. * @Author 公众号: IT三明治
  9. * @Date 2024/6/16
  10. * @Description:
  11. */
  12. @Slf4j
  13. @Service
  14. @Component
  15. public class Demo2ServiceImpl implements Demo2Service {
  16.     @Override
  17.     @Async
  18.     public void feedUserInfoToOtherService(String userId) throws InterruptedException {
  19.         for (int i = 0; i < 40; i++) {
  20.             log.info("Demo2Service update {} login info to other services, service num: {}", userId, i+1);
  21.             Thread.sleep(1000);
  22.         }
  23.     }
  24. }
复制代码

  1. package com.it.sandwich.service.impl;
  2. import com.it.sandwich.service.Demo2Service;
  3. import lombok.extern.slf4j.Slf4j;
  4. import org.springframework.scheduling.annotation.Async;
  5. import org.springframework.stereotype.Component;
  6. import org.springframework.stereotype.Service;
  7. /**
  8. * @Author 公众号: IT三明治
  9. * @Date 2024/6/16
  10. * @Description:
  11. */
  12. @Slf4j
  13. @Service
  14. @Component
  15. public class Demo1ServiceImpl implements Demo1Service {
  16.     @Override
  17.     @Async
  18.     public void feedUserInfoToOtherService(String userId) throws InterruptedException {
  19.         for (int i = 0; i < 35; i++) {
  20.             log.info("Demo1Service update {} login info to other services, service num: {}", userId, i+1);
  21.             Thread.sleep(1000);
  22.         }
  23.     }
  24. }
复制代码
添加两个@Async方法,验证全局生效。

  1. package com.it.sandwich.controller;
  2. import com.it.sandwich.base.ResultVo;
  3. import com.it.sandwich.service.Demo1Service;
  4. import com.it.sandwich.service.Demo2Service;
  5. import lombok.extern.slf4j.Slf4j;
  6. import org.springframework.web.bind.annotation.GetMapping;
  7. import org.springframework.web.bind.annotation.PathVariable;
  8. import org.springframework.web.bind.annotation.RequestMapping;
  9. import org.springframework.web.bind.annotation.RestController;
  10. import javax.annotation.Resource;
  11. /**
  12. * @Author 公众号: IT三明治
  13. * @Date 2024/6/16
  14. * @Description:
  15. */
  16. @Slf4j
  17. @RestController
  18. @RequestMapping("/api")
  19. public class DemoController {
  20.     @Resource
  21.     Demo1Service demo1Service;
  22.     @Resource
  23.     Demo2Service demo2Service;
  24.     @GetMapping("/{userId}")
  25.     public ResultVo<Object> getUserInfo(@PathVariable String userId) throws InterruptedException {
  26.         log.info("userId:{}", userId);
  27.         demo1Service.feedUserInfoToOtherService(userId);
  28.         demo2Service.feedUserInfoToOtherService(userId);
  29.         for (int i = 0; i < 30; i++) {
  30.             log.info("updating user info for {}, waiting times: {}", userId, i+1);
  31.             Thread.sleep(1000);
  32.         }
  33.         return ResultVo.ok();
  34.     }
  35. }
复制代码

  1. package com.it.sandwich.config;
  2. import org.springframework.context.annotation.Bean;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.scheduling.annotation.EnableAsync;
  5. import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
  6. import java.util.concurrent.Executor;
  7. /**
  8. * @Author 公众号: IT三明治
  9. * @Date 2024/6/16
  10. * @Description:
  11. */
  12. @Configuration
  13. @EnableAsync
  14. public class AsyncConfig {
  15.     @Bean
  16.     public Executor taskExecutor() {
  17.         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
  18.         executor.setCorePoolSize(2); // 设置核心线程数
  19.         executor.setMaxPoolSize(5); // 设置最大线程数
  20.         executor.setQueueCapacity(100); // 设置队列容量
  21.         executor.setThreadNamePrefix("sandwich-async-pool-"); // 自定义线程名称前缀
  22.         executor.setWaitForTasksToCompleteOnShutdown(true); // 设置线程池关闭时是否等待任务完成
  23.         executor.setAwaitTerminationSeconds(60); // 设置等待时间,如果你需要所有异步线程的安全退出,请根据线程池内敢长线程处理时间配置这个时间
  24.         return executor;
  25.     }
  26. }
复制代码
3. 验证代码


  1. Administrator@USER-20230930SH MINGW64 /d/git/micro-service-logs-tracing
  2. $ curl http://localhost:8080/api/sandwich
复制代码

日志完美验证了我们的期待。 我设置的“sandwich-async-pool-”线程名前缀也在两个线程日志中体现了。进一步证明AsyncConfig对全部@Async注解修饰的异步线程全局有效。
这是为什么呢?
4. AsyncConfig设置代码分析

当我在Spring设置中通过@Bean界说了一个ThreadPoolTaskExecutor实例,而且在同一设置类或其他被扫描到的设置类中启用了@EnableAsync注解时,这个自界说线程池会自动与Spring的异步任务执行机制关联起来。这一过程背后的原理涉及到Spring的异步任务执行器(AsyncConfigurer接口)的自动设置和代理机制,具体原因如下:
     综上所述,自界说的ThreadPoolTaskExecutor之以是能成为Spring异步任务执行的默认线程池,是因为Spring的自动设置逻辑、AOP代理机制以及通过设置明确指定了这个线程池的使用。
至此,graceful shutdown已经可以使多线程,高并发的项目在做release的时候,线程安全性得到保障。 特别是一些长处置惩罚的schedul job项目(此中好多job为了提交效率,用了异步机制),经过这样优化之后,release的信心是不是加强了好多。
写文章不容易,假如对您有用,请点个关注支持一下博主再走。谢谢。
假如有更好见解的朋友,请在评论区给出您的引导意见,感谢!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4