省流:通过线程池优化查询的业务逻辑,提高接口的效率,但需注意线程池的参数配置(阻塞队列大小,拒绝策略),避免带来其他的问题
问题背景:我们在使用系统的过程中,会遇到一些导出数据的场景,比如导出某个人最近一天的所有会议记录,以及会议的参会人信息
查询会议记录的业务逻辑
第一步 先根据用户查询查询用户的会议列
第二步 根据第一步查询出来的会议列表的会议信息,去循环查询每个会议的与会人信息
第三步 组合结果返回
业务代码(注:代码为模拟代码,并不涉及真实生产的代码)
并发为5时,请求耗时为平均在2000ms左右
并发50时,请求耗时为平均3000+ms左右
并发500时,请求耗时为平均4000+ms左右
随着并发量的上涨,查询数据的耗时越来越长,且并发为5时就已经达到2000ms左右了,耗时巨长
通过观察业务逻辑,循环查询每个会议的与会人信息的时候,并没有逻辑上的关联,也就是说,查询每个会议的与会人信息可以看作一个独立的过程,我们可以考虑用单独的线程去执行这个查询,通过并发来提高整体的效率。 改进后的流程为 第一步 先根据用户查询查询用户的会议列表 第二步 根据第一步查询出来的会议列表的会议信息,把每个会议的与会人查询提交到线程池当中去执行 第三步 组合结果返回
业务代码(注:代码为模拟代码,并不涉及真实生产的代码)
线程池参数
核心线程数为4(一般IO类型任务核心线程设为cpu * 2,计算类型任务时,核心线程设为cpu + 1,查询数据库或者访问第三方接口都数据IO类型的任务)
最大线程数设置为20
空闲线程存活时间 5分钟
任务队列大小 500
拒绝策略 直接拒绝
改进后 并发为5时,耗时为100+ms,速度得到质的提升
并发为50时,初期耗时为100ms,但是随着并发量的上升,后续的耗时逐渐开始变大 ,最大的时候为1000+ms,此时还出现阻塞队列中的任务,获取超时的问题,此时已经开始影响到了正常的业务,有部分的用户开始导出缺失的数据了
耗时增大 超时问题
并发为500时,也是同样的初期耗时较低,后续耗时变大,此时阻塞队列出现了大量超时的问题,大量用户已经开始导出空白的数据了,问题更加严重了
目前线程池出现的问题
问题一:后续耗时会变大?
问题二:阻塞队列已满,且出现大量队列中的任务超时问题,直接导致用户导出了空白的数据?
解决方案:
问题一 :耗时开始增大是正常的现象,因为当流量变大的时候,服务处理能力到最高后无法提升了,必然会有开始变慢的问题,影响不大,可通过限流或者是提醒用户稍等重试的方式较低影响
问题二 : 这个问题的本质是因为线程池参数设置得不合理
1、队列设置错误,该场景下,需要充分利用线程资源,将线程放入队列只会徒增等待的时间,导致等待队列里的任务全部超时抛出异常
2、拒绝策略设置错误,直接拒绝任务会抛出异常导致主流程中断,但此时主流程可能已经提交了部分任务,导致无效任务的提交
优化后参数
相比于之前,只修改了两个参数
1.把原来的任务队列设置为SynchronousQueue ,SynchronousQueue 是一个特殊的队列,其最大容量是1。也就是说,任何一次插入操作都必须等待一个相应的删除操作,反之亦然。如果没有相应的操作正在进行,则该线程将被阻塞,也就是说当线程池达到最大线程后,就不在接受新的任务了,解决阻塞队列内的任务超时的问题
2.拒绝策略改为CallerRunsPolicy,当阻塞队列满了,由提交任务的线程去完成执行,原来的流程中,主线程提交完任务后就阻塞等待了,浪费了主线程的线程资源,可利用主线程的资源,当阻塞队列满了,让主线程来执行当前任务,充分利用主线程的资源
执行 并发量为5时,执行耗时为100ms左右,和原来参数表现基本一致
并发量为50时,执行耗时为100+ms左右,最高也只到150ms,且没有出现任何异常
并发量为500时,执行耗时为300+ms左右,最高也只到500+ms,且没有出现任何异常