09小节:通过线程池和延时队列优化接口响应时 间
作者:程序员马丁
note
热门项目实战社群,收获国内众多知名公司面试青睐,近千名同学面试成功!助力你在校招或社招上拿个offer。
通过线程池和延时队列优化接口响应时间,元数据信息:
- 什么是牛券oneCoupon:https://t.zsxq.com/pAWgS
- 代码仓库:https://gitcode.net/nageoffer/onecoupon —— 申请项目权限参考上述牛券项目链接
- 章节难度:★★★☆☆ - 较难
- 视频地址:文档先行视频次之
©版权所有 - 拿个offer-开源&项目实战星球专属学习项目,依据《中华人民共和国 著作权法实施条例》和《知识星球产权保护》,严禁未经本项目原作者明确书面授权擅自分享至 GitHub、Gitee 等任何开放平台。违者将面临法律追究。
内容摘要:这节课我们通过线程池和 Redis 延迟队列的形式优化 EasyExcel 解析百万数据接口耗时问题,接口响应从从 5 秒提升到毫秒级。
课程目录如下所示:
- 业务背景
- Git 分支
- 线程池异步解析 Excel 行数
- Redis 延时队列兜底任务
- 添加 Spring 事务
- 文末总结
业务背景
在上一节中,我们通过 EasyExcel 解析百万数据量的 Excel 行数,避免了 JVM 内存占用过多问题。但是末了还有一个小问题没有说,那就是接口响应太慢,百万数据量需要解析 5 秒,这种在后管系统里不是不能接受,但是能优化还是要优化。在这节课我们通过线程池和 Redis 延迟队列的形式优化接口响应时间。
Git 分支
20240823_optimize_create-coupon-task_threadpool-delayqueue_ding.ma
线程池异步解析 Excel 行数
1. 创建线程池
创建一个公共线程池,因为咱们这个逻辑比较简单,所以直接定义即可。
@Service
@RequiredArgsConstructor
public class CouponTaskServiceImpl extends ServiceImpl<CouponTaskMapper, CouponTaskDO> implements CouponTaskService {
private final ExecutorService executorService = new ThreadPoolExecutor(
Runtime.getRuntime().availableProcessors(),
Runtime.getRuntime().availableProcessors() << 1,
60,
TimeUnit.SECONDS,
new SynchronousQueue<>(),
new ThreadPoolExecutor.DiscardPolicy()
);
// ......
}
有些同学可能习惯使用 Executors
工具类直接创建线程池,这种是不推荐的。虽然 Executors
提供了创建线程池的便捷方法,然而,Executors
基于默认配置创 建的线程池可能并不适合所有场景,这里我们说下每个方法创建的线程池都有哪些弊端:
newFixedThreadPool
和newSingleThreadExecutor
:这两种固定大小的线程池使用无界的LinkedBlockingQueue
作为工作队列。当任务提交速度超过处理速度时,工作队列会不断增长,可能导致内存溢出。newScheduledThreadPool
:虽然最大线程数是Integer
最大值,但是因为阻塞队列是无界的,所以核心问题同上。newCachedThreadPool
:核心线程数为 0,使用同步的SynchronousQueue
,并且允许创建无限数量的线程。在高并发情况下,可能会创建大量线程,导致系统资源耗尽,甚至使系统崩溃。
扩展知识,线程池处理逻辑如下:
2. 线程池参数解析
解析下我们线程池中的参数为什么这么设置:
- corePoolSize:因为属于后管任务,大概率不会很频繁,所以直接取服务器 CPU 核数。
- maximumPoolSize:运行任务属于 IO 密集型,最大线程数直接服务器 CPU 核数 2 倍。
- workQueue:理论上说我们不会有阻塞的情况,因为设置的线程数不少,所以如果使用不存储任务的同步队列。
- handler:如果线程数都在运行,直接将任务丢弃即可,因为我们还有延时队列兜底。