JSF业务线程池使用JDK的线程池技术,缺省情况下采用Cached模式(核心线程数20,最大线程数200)。此外,还提供了Fixed固定线程大小的模式,两种模式均可设置请求队列大小。
本文旨在通过一个简化场景(“单服务应用”)下的负载测试,为“JSF业务线程池大小配置”提供基准测试结果,并形成一些普遍适用的结论。
本文的目标读者包括需要合理配置JSF线程大小的压测工程师、开发部署运维工程师以及架构师。本文不涉及JSF服务端的其他配置项,也不针对“复合服务应用”的合理配置进行探讨。你可以利用本文提供的结论,作为设计压测用例或评估业务线程池大小的基本方法的参考,以便在实践中合理配置JSF业务线程池大小。需要注意的是,JSF业务线程池大小的合理配置应该基于高保真的负载测试结果。
“单服务应用”指应用仅包含一个提供接口,且接口中仅有一个方法。
“复合服务应用”则指应用包含多个提供接口或一个接口中含有多个方法。
本次基准测试选取了USF3.0权限系统,将其定制化为一个单一的服务提供者,仅对该提供者的一个方法进行了测试,因此可以看作是一个“单服务应用”。测试中将CPU作为基准测试的核心资源,并考虑到JVM垃圾收集器的影响,采用了简单的测试数据以保证服务每次调用的一致性,并确保YGC具有规律性(即固定调用量会导致一次30+ms的YGC),无FGC的影响。
测试用例的设计中,所有依赖的服务资源都无限制,以确保测试过程中服务的可用率达到100%。我们的关键性能指标是TP99,即服务响应时长的99%必须小于10ms。
为了测试不同线程池模式下的性能表现,我们使用了JSF线程池的Cached和Fixed两种模式,并针对每种模式进行了多组测试,以得出在满足TP99<10ms的前提下,系统最大的负载情况。
测试应用:USF3.0权限系统(定制化处理)
测试服务:com.jd.susf.service.api.SusfPermissionService#findUserInfo,根据用户信息从Redis中查询一条数据返回的服务。
硬件配置:单台4C 8G
测试方法:在Forcebot系统采用了阶梯发压的方式对JSF业务线程池在Cached和Fixed模式下进行了系统负载测试
拟定SLA要求:服务响应时长的TP99<10ms
注:我们对USF3.0权限系统进行了定制,调整了服务提供方的配置数据,仅保留了 com.jd.susf.service.api.SusfPermissionService。
图:JSF默认线程池(cached, threads=200)在不同并发用户数(1-200)下的系统负载图
并发用户数 | TP99 | 吞吐量TPS | CPU利用率(%) |
---|---|---|---|
1~23 | <8ms | 线性增长 | 线性增长 |
24 | 8ms | 6553 | 99.62 |
25 | 11ms | 6607 | 99.83 |
26~79 | 迅速增长 | 缓慢增长 | 99+ |
80 | 74ms | 6928 | 99.82 |
81~199 | 缓慢增加 | 缓慢下降 | 99.82 |
200 | 99ms | 6230 | 99.94 |
小结:默认的JSF线程池配置存在很大的风险。系统最大可支持24个并发,超过24个并发SLA就无法满足。
图:JSF固定线程池(fixed+队列)在不同并发用户数(1-50)下的系统负载图
JSF业务线程数 | 可支持的最大并发用户数 | TP值(50/90/99/999) | 吞吐量(TPS) | CPU最大利用率(%) |
---|---|---|---|---|
4 | 11 | 7/8/10/18 | 1531 | 27.67 |
8 | 25 | 8/8/10/18 | 3113 | 46.45 |
16 | 50 | 8/8/10/21 | 6228 | 87.97 |
20 | 23 | 3/4/10/15 | 6409 | 99.92 |
24 | 22 | 3/4/7/15 | 6178 | 99.86 |
25 | 22 | 3/4/6/15 | 6182 | 98.83 |
表:JSF固定业务线程池(fixed+队列)在满足TP99<10ms的系统最大负载(最大并发用户数)
小结:
① 在fixed线程模式下,CPU的利用率存在使用上限。
② 队列的使用可以有效增加系统对并发量的支持,同时也会带来吞吐量的提升。然而,由于任务在队列中等待,服务的响应时间会出现“水涨船高”的现象,存在一定风险。
图:JSF固定线程池(fixed)模式下,系统最大并发用户数时的系统负载
JSF业务线程数 | 并发用户数 | TP99 | 吞吐量(TPS) | CPU最大利用率(%) |
---|---|---|---|---|
4 | 4 | 5 | 1063 | 20.26 |
8 | 8 | 5 | 2216 | 36.62 |
16 | 16 | 6 | 4262 | 68.56 |
20 | 20 | 5 | 5550 | 86.22 |
24 | 24 | 8 | 6711 | 99.62 |
25 | 25 | 16 | 6644 | 98.77 |
26 | 26 | 19 | 6744 | 99.93 |
小结:综合固定线程池(fixed)的性能表现,需要设置一个合理的线程数大小来平衡CPU资源的充分利用和满足SLA的需求,线程数过小会导致CPU资源浪费,线程数过大则无法满足SLA
根据测试结果和数据分析,我们得出以下结论:
综上所述,线程池大小的合理配置需要结合业务需求和系统资源情况进行评估和测试,并预留合理的buffer空间,以保证系统稳定运行和满足用户的SLA。
并发用户数:同时发起请求的用户数。
TP值(50/90/99/999):客户端的TP值,单位ms,数据来源于Forcebot。
吞吐量TPS:数据来源于Forcebot。
CPU利用率(%):数据来源于PFinder。
JSF业务线程数:JSF业务线程池的线程数,如:<jsf:server id="jsf" protocol="jsf" threadpool="fixed" threads="16" />
fixed/cached:JSF业务线程池的线程池类型,如:<jsf:server id="jsf" protocol="jsf" threadpool="fixed" threads="200"/>
作者:京东物流 刘江波
来源:京东云开发者社区 自猿其说Tech 转载请注明来源
由于数据库的承载能力是有限的,当业务增长量达到一定规模后,数据库的性能就会达到瓶颈。于是产生了分库分表的解决方案,本文将详细讲解什么是分库分表,以及分库分表的原因和可能产生的问题。
我们在进行 Web 应用开发时,时常需要对请求进行拦截或处理,故 Spring 为我们提供了过滤器和拦截器来应对这种情况。那么两者之间有什么不同呢?本文将详细讲解两者的区别和对应的使用场景。