SpringBoot使用异步线程池实现生产环境批量数据推送
前言
SpringBoot使用异步线程池:
1、编写线程池配置类,自定义一个线程池;
2、定义一个异步服务;
3、使用@Async注解指向定义的线程池;
这里以我工作中使用过的一个案例来做描述,我所在公司是医疗行业,敏感数据需要上报到某监管平台,所以有一个定时任务在流量较小时(一般是凌晨后)执行上报行为。但特殊时期会存在一定要在工作时间大批量上报数据的情况,且要求短时间内就要完成,此时就考虑写一个专门的异步上报接口手动执行,利用线程池上报,极大提高了速度。
编写线程池配置类
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor; /**
* 类名称:ExecutorConfig
* ********************************
* <p>
* 类描述:线程池配置
*
* @author guoj
* @date 2021-09-07 09:00
*/
@Configuration
@EnableAsync
@Slf4j
public class ExecutorConfig {
/**
* 定义数据上报线程池
* @return
*/
@Bean("dataCollectionExecutor")
public Executor dataCollectionExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // 核心线程数量:当前机器的核心数
executor.setCorePoolSize(
Runtime.getRuntime().availableProcessors()); // 最大线程数
executor.setMaxPoolSize(
Runtime.getRuntime().availableProcessors() * 2); // 队列大小
executor.setQueueCapacity(Integer.MAX_VALUE); // 线程池中的线程名前缀
executor.setThreadNamePrefix("sjsb-"); // 拒绝策略:直接拒绝
executor.setRejectedExecutionHandler(
new ThreadPoolExecutor.AbortPolicy()); // 执行初始化
executor.initialize(); return executor;
} }PS:
1)、需要注意,这里一定要自己定义ThreadPoolTaskExecutor线程池,否则springboot的异步注解会执行默认线程池,存在线程阻塞导致CPU飙高及内存溢出的风险。这一点可以参考阿里开发手册,线程池定义这块明确提到了这一点;
2)、在@Bean注解中定义线程池名称,后面异步注解会用到。
编写异步服务
/**
* 异步方法的服务, 不影响主程序运行。
*/
@Service
public class AsyncService { private final Logger log = LoggerFactory.getLogger(AsyncService.class); /**
* 发送短信
*/
@Async("sendMsgExecutor")
public void sendMsg(String access_token, Consult item, Map<String, String> configMap) {
// 此处编写发送短信业务
// 1、buildConsultData();
// 2、sendMsg();
} /**
* 发送微信订阅消息
*/
@Async
public void sendSubscribeMsg(String access_token, Consult item, Map<String, String> configMap) {
// 此处编写发送微信订阅消息业务
// 1、buildConsultData();
// 2、sendSubscribeMsg();
} /**
* 数据并上报
*/
@Async("dataCollectionExecutor")
public void buildAndPostData(String access_token, Consult item, Map<String, String> configMap) {
// 此处编写上报业务,如拼接数据,然后执行上报。
// 1、buildConsultData();
// 2、postData();
}
}
异步批量上报数据
@Autowired
private AsyncService asyncService; /**
* 手动上报问诊记录,线程池方式。
*/
public void manualUploadConsultRecordsAsync(String channel, Date startTime, Date endTime) { // 查询指定时间内的问诊记录
List<Consult> consultList = consultService
.findPaidListByChannelAndTime(channel, startTime, endTime, configMap.get("serviceId")); if (!CollectionUtils.isEmpty(consultList)) { log.debug("[SendWZDataService][manualUploadConsultRecordsAsync]>>>> 手动上报问诊记录, 一共[{}]条", consultList.size()); consultList.forEach((item) -> {
try {
// 异步调用,使用线程池。
asyncService.buildAndPostData(access_token, item, configMap);
} catch (Exception ex) {
log.error("[SendWZDataService][manualUploadConsultRecordsAsync]>>>> 手动上报问诊记录发生异常: ", ex);
}
}); }
}
总结
以上方式已经在生产环境运行,在工作时间内执行过很多次,一次数万条记录基本是几分钟内就全部上报完毕,而正常循环遍历时一次大概需要半个小时左右。 线程池的使用方式往往来源于业务场景,如果类似的业务不存在紧急处理的情况,大体还是以任务调度执行为主,因为更安全。如果存在紧急处理的情况,那么使用SpringBoot+线程池的方式不仅能节省非常多的时间,且不占用主线程的执行空间。 喜欢就点个关注吧~~
SpringBoot使用异步线程池实现生产环境批量数据推送的更多相关文章
- Springboot的异步线程池
1:定义线程池 @EnableAsync @Configuration class TaskPoolConfig { @Bean("taskExecutor") public Ex ...
- springboot+@async异步线程池的配置及应用
示例: 1. 配置 @EnableAsync @Configuration public class TaskExecutorConfiguration { @Autowired private Ta ...
- spring boot:使用async异步线程池发送注册邮件(spring boot 2.3.1)
一,为什么要使用async异步线程池? 1,在生产环境中,有一些需要延时处理的业务场景: 例如:发送电子邮件, 给手机发短信验证码 大数据量的查询统计 远程抓取数据等 这些场景占用时间较长,而用户又没 ...
- springBoot服务整合线程池ThreadPoolTaskExecutor与@Async详解使用
ThreadPoolExecutor:=======这个是java自己实现的线程池执行类,基本上创建线程池都是通过这个类进行的创建.ThreadPoolTaskExecutor:========这个是 ...
- 使用C++11 开发一个半同步半异步线程池
摘自:<深入应用C++11>第九章 实际中,主要有两种方法处理大量的并发任务,一种是一个请求由系统产生一个相应的处理请求的线程(一对一) 另外一种是系统预先生成一些用于处理请求的进程,当请 ...
- 使用C++11实现一个半同步半异步线程池
前言 C++11之前我们使用线程需要系统提供API.posix线程库或者使用boost提供的线程库,C++11后就加入了跨平台的线程类std::thread,线程同步相关类std::mutex.std ...
- c++11 实现半同步半异步线程池
感受: 随着深入学习,现代c++给我带来越来越多的惊喜- c++真的变强大了. 半同步半异步线程池: 事实上非常好理解.分为三层 同步层:通过IO复用或者其它多线程多进程等不断的将待处理事件加入到队列 ...
- Spring Boot系列二 Spring @Async异步线程池用法总结
1. TaskExecutor Spring异步线程池的接口类,其实质是java.util.concurrent.Executor Spring 已经实现的异常线程池: 1. SimpleAsyncT ...
- (原创)C++半同步半异步线程池2
(原创)C++半同步半异步线程池 c++11 boost技术交流群:296561497,欢迎大家来交流技术. 线程池可以高效的处理任务,线程池中开启多个线程,等待同步队列中的任务到来,任务到来多个线程 ...
随机推荐
- 【LeetCode】289. Game of Life 解题报告(Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 日期 题目地址:https://leetcode.c ...
- 1114 - Easily Readable
1114 - Easily Readable PDF (English) Statistics Forum Time Limit: 2 second(s) Memory Limit: 32 MB ...
- 序列化之serialVersionUID
serialVersionUID作用: 序列化时为了保持版本的兼容性,即在版本升级时反序列化仍保持对象的唯一性. 序列化ID起着关键的作用,java的序列化机制是通过在运行时判断类的serialVer ...
- fork之后,子进程从父进程那继承了什么(转载)
转载自:https://blog.csdn.net/xiaojun111111/article/details/51764389 知道子进程自父进程继承什么或未继承什么将有助于我们.下面这个名单会因为 ...
- TensorFlow.NET机器学习入门【7】采用卷积神经网络(CNN)处理Fashion-MNIST
本文将介绍如何采用卷积神经网络(CNN)来处理Fashion-MNIST数据集. 程序流程如下: 1.准备样本数据 2.构建卷积神经网络模型 3.网络学习(训练) 4.消费.测试 除了网络模型的构建, ...
- C++ 虚函数和友元
虚函数具有动态联编性,在类族中有强大功能:友元函数具有跨类访问的功能,本质却是一种对封装的破坏. 先看这样一个例子: #include<iostream> using namespace ...
- Ranger-Solr审计日志安装
使用RangerAdmin安装solr,基于已有的solr环境安装,主要是在solr创建用于存储数据的CoreAdmin. # 1.解压安装 在solr的安装机器上面,获取RangerAdmin并且解 ...
- C/C++ Qt 运用JSON解析库 [基础篇]
JSON是一种简单的轻量级数据交换格式,Qt库为JSON的相关操作提供了完整的类支持,使用JSON解析文件之前需要先通过TextStream流将文件读入到字符串变量内,然后再通过QJsonDocume ...
- 深入 Laravel 内核之外观模式(门面模式)
门面模式核心内容: 客户端与子系统的通信通过外观对象进行: 外观对象封装一系列子系统的具体对应方法,对客户端只需暴露一个单一的入口方法: 客户端通过访问外观对象即可调用子系统的基础方法,无需关心子系统 ...
- Unity3D开发入门教程(二)—— Lua入门
五邑隐侠,本名关健昌,12年游戏生涯. 本教程以 Unity 3D + VS Code + C# + tolua 为例. 如果你还没有编程基础,建议你先学习一些编程基础.本文不是完全菜鸟教程,主要针对 ...