设置线程数(转)

前言

“不好了,线上服务器超时严重,请求非常慢,好像报连接数too many了,怎么办?“小伙伴们在反馈。一般我们的技术老大的处理方式,把连接数和线程池调大点,重启,再观察。

往往这个方式是应急措施,治标不治本,因为不知道问题的原因

有个严重误区,以为线程池设置太小了,调大点请求就会快了。

今天老顾就带着小伙伴们沟通一下,线程池的大小应该如何合理的设置其大小?

问题

如果有两个任务需要处理,一个任务A,一个任务B

方案一:一个线程执行任务A和B,A执行完后,执行B
方案二:两个线程A和B去执行任务A 和 B,同时进行

哪个方案会快点?应该很多人会回答,肯定是方案二啊,多线程并行去处理任务A和B,肯定快啊。是这样吗?回答这个问题之前,先带着大家去回顾梳理一下。

线程执行

线程的执行,是由CPU进行调度的,一个CPU在同一时刻只会执行一个线程,我们看上去的线程A 和 线程B并发执行。

为了让用户感觉这些任务正在同时进行,操作系统利用了时间片轮转的方式,CPU给每个任务都服务一定的时间,然后把当前任务的状态保存下来,在加载下一任务的状态后,继续服务下一任务。任务的状态保存及再加载,这段过程就叫做上下文切换

上下文切换过程是需要时间的;现在我们来看一下上面的问题,小伙伴们再看一下是哪个方案快呢?是不是有些小伙伴们会说方案一,因为不需要线程切换;方案二需要来回切换这两个线程,耗时会多点。

小伙伴们心中此时是不是会有疑惑,那为什么会有多线程?先不急,再往下看。

为什么要多线程

小伙伴想想在我们真实业务中,我们是什么流程?

工作多年,你真的会设置线程数吗?

 

上图的流程:

1、先发起网络请求

2、web服务器解析请求

3、请求后端的数据库获取数据

4、获取数据后,进行处理

5、把处理结果放回给用户

这个是我们处理业务的时候,常规的请求流程;我们看一下整个过程涉及到什么计算机处理。

1、网络请求----->网络IO

2、解析请求----->CPU

3、请求数据库----->网络IO

4、mysql查询数据----->磁盘IO

5、mysql返回数据----->网络IO

6、数据处理----->CPU

7、返回数据给用户----->网络IO

讲到这里,小伙伴们是不是感觉又不乱了,在真实业务中我们不单单会涉及cpu计算,还有网络IO和磁盘IO处理,这些处理是非常耗时的。如果一个线程整个流程是上图的流程,真正涉及到CPU的只有2个节点,其他的节点都是IO处理,那么线程在做IO处理的时候,CPU就空闲出来了,CPU的利用率就不高

小伙伴们现在知道多线程的用处了吧,对,就是为了提升CPU利用率

提升QPS/TPS

衡量系统性能如何,主要指标系统的(QPS/TPS)

QPS/TPS:每秒能够处理请求/事务的数量

并发数:系统同时处理的请求/事务的数量

响应时间:就是平均处理一个请求/事务需要时长

QPS/TPS = 并发数/响应时间

上面公式代表并发数越大,QPS就越大;所以很多人就会以为调大线程池,并发数就会大,也会提升QPS,所以才会出现一开始前言所说的,大多数人的误区。

其实QPS还跟响应时间成反比,响应时间越大,QPS就会越小。

虽然并发数调大了,就会提升QPS,但线程数也会影响响应时间,因为上面我们也提到了上下文切换的问题,那怎么设置线程数的呢?

如何设置线程数

那我们如何分配线程?我们提供一个公式:

最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目

备注这个公式也是前辈们分享的,当然老顾看了淘宝前台系统优化实践的文章,和上面的公式很类似,不过在CPU数目那边,他们更细化了,上面的公式只是参考。不过不管什么公式,最终还是在生产环境中运行后,再优化调整。

我们继续上面的任务,我们的服务器CPU核数为4核,一个任务线程cpu耗时为20ms,线程等待(网络IO、磁盘IO)耗时80ms,那最佳线程数目:( 80 + 20 )/20 * 4 = 20。也就是设置20个线程数最佳。

从这个公式上面我们就得出,线程的等待时间越大,线程数就要设置越大,这个正好符合我们上面的分析,可提升cpu利用率。那从另一个角度上面说,线程数设置多大,是根据我们自身的业务的,需要自己去压力测试,设置一个合理的数值。

基础常规标准

那我们小伙伴们会问,因为很多业务集中到一个线程池中,不像上面的案例比较简单,事实上业务太多,怎么设置呢?这个就是要去压力测试去调整。不过我们的前辈已经帮我们总结了一个基础的值(最终还是要看运行情况自行调整)

1、CPU密集型:操作内存处理的业务,一般线程数设置为:CPU核数 + 1 或者 CPU核数*2。核数为4的话,一般设置 5 或 8

2、IO密集型:文件操作,网络操作,数据库操作,一般线程设置为:cpu核数 / (1-0.9),核数为4的话,一般设置 40

总结

今天介绍了线程数大小的设置,一些小伙伴们的误区。讲到这里我们小伙伴们是不是对线程有了更新的理解,不像之前那么粗暴,应该要去分析为什么这么慢,系统的瓶颈出现在什么地方,减少瓶颈的耗时。

 

 

线程池容量设置

线程池的corePoolSize设置是整个线程池中最关键的参数,设置太小会导致线程池的吞吐量不足,因为新提交的任务需要排队或者被handler处理掉(取决于拒绝策略);设置太大可能会耗尽计算机的CPU和内存资源。

CPU bound

在CS中,CPU密集型任务的执行时间主要取决于CPU的时间。CPU密集型任务的线程数通常设置为CPU核心数+1 ,《Java Concurrency in Practice》(由Doug. Lea等人撰写)给出了如下的解释:

Even computeͲintensive threads occasionally take a page fault or pause for some other reason, so an"extra" runnable thread prevents CPU cycles from going unused when this happens.

即比CPU核心数多出来的一个线程是为了防止线程偶发的page fault或者由于其他原因导致的任务暂停,此时CPU就会处于空闲状态,而在这种情况下多出来的一个线程就可以充分利用CPU的这个空闲时间。

IO bound

IO密集型任务是指任务的执行时间主要取决于IO的时间。在笔者实习期间遇到的业务场景中大部分属于IO bound。而对于IO bound,书中则给出了如下的计算公式:

最佳线程数 = CPU数量 * CPU利用率 *(线程等待时间/线程CPU时间 + 1)

实际最优参数

在实际操作中,由于公式中线程等待时间和线程CPU时间不好估算,以及系统中存在其他的阻塞情况,这样计算出来的最佳线程数往往不是生产环境下的最佳线程数。为了提高精确度,笔者利用有赞内部的压测系统对代码进行压测,通过多次调整计算出来的最佳线程数和观察压测结果(系统负载、接口RT、TPS等指标)来判断线程数是否符合期望。

在压测的过程中发现,当线程数量设置的更合理时TPS更高且接口的RT较低;而线程池设置过大导致TPS下降和RT上涨。由于RT和TPS不太方便直接给出,这里仅展示系统负载这一指标的压测结果。

当线程池设置过大时:

工作了这么久,你知道Java线程池容量应该设置多少么

图2 压测时把线程池参数设置得很大

当线程池设置较为合理时:

工作了这么久,你知道Java线程池容量应该设置多少么

图3 线程数较为合理

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值