记一次基于Docker的性能测试

发表于:2019-08-16来源:juejin作者: 冰箱没有油点击数: 标签:
断断续续忙碌了几个月,终于自己写的开源项目算是有了雏形,打包成Docker image发布到AWS EC2后,写代码算是告一段落。随之而来的问题就是“我的项目能够支撑多少QPS” ,由于用了Do

一句话结论

对于跑在单核CPU上的运算类API, 根据业务需求(最大响应时间)来调试找到最大线程数,然后依据线程数调试出heap大小(主要看年老代的回收次数)

若有理解不到位之处,请在评论区留言,跪谢!

背景

断断续续忙碌了几个月,终于自己写的开源项目算是有了雏形,打包成Docker image发布到AWS EC2后,写代码算是告一段落。随之而来的问题就是“我的项目能够支撑多少QPS” ,由于用了Docker, 即变成了“我的项目基于Docker的配置能够支撑多少QPS”, 更进一步细化这个问题的话,有以下几点:

  • 因为我用了IaaS来创建Linux服务器(选用了Ubuntu)基本配置为 1G RAM 1CPU (2.5GHz)10G+ 硬盘空间(非SSD) 
  • 当然我并不希望一个Docker container就把上述资源全部占用掉。另一个原因是目前IaaS所提供的内存最小单元是500M,算上系统其他进程的开销,可供一个Docker image 的最大内存资源我定在了400M

说句题外话,我认为按照服务商提供的最小单元来划分的好处在于:减少开销。500M的费用只有1G的一半,而且将来项目动态伸缩的灵活度高,粒度更小。

  • 项目是一个OAuth2的Spring boot实现,本身对于IO的要求不高、都是短链接。因为要生成JWT令牌,主要压力在CPU。用了内置的Tomcat,多线程在一个CPU上跑,请求数目一多,99%使用率简直是家常便饭

第一步:确定性能测试的指标(benchmark)

俗话说的好, 抛开业务需求来谈IT就是耍流氓。

项目是开源项目,业务需求那就只好我自己定了,一般来说我们并不希望用户登录过快(并且并发登录的情况虽然有但是确实比较少见),这次的api (oauth/token) 我定在了2秒的最大值,以此为基础来找出性能瓶颈。

第二步:确定可调参数

那么在不改动项目代码的前提下,可以调整哪些参数来提升性能的呢?

  • JVM 相关参数,例如 GC、Heap、Thread stack
  • Tomcat 相关参数,例如 max-threads、max-connections、accept-count

对于计算为主的项目,主要关注点还是在max-threads,设置合理的话可以减少CPU上下文切换带来的性能开销,以及合适的Heap大小来避免频繁触发GC所造成性能抖动。

第三步:开测 (多图预警)

虽然标题是基于Docker的性能测试,但是我还是想对比一下用与不用Docker上的性能差异,所以测试会分为两个部分, 以下为jre的基本信息

ubuntu@ip-xxx-xx-xx-xxx:~$ java -version
openjdk version "11.0.4" 2019-07-16
OpenJDK Runtime Environment (build 11.0.4+11-post-Ubuntu-1ubuntu218.04.3)
OpenJDK 64-Bit Server VM (build 11.0.4+11-post-Ubuntu-1ubuntu218.04.3, mixed mode, sharing)复制代码

ubuntu jre 信息

/ # java -version
openjdk version "11" 2018-09-25
OpenJDK Runtime Environment 18.9 (build 11+28)
OpenJDK 64-Bit Server VM 18.9 (build 11+28, mixed mode)复制代码

docker jre 信息

3.1 无Docker下的性能测试

3.1.1 Heap 固定,不同线程数关系图

50m Heap大小在100个线程的情况下OOM,所以这里为0

3.1.2 线程数固定,不同Heap大小下关系图

3.2 基于Docker的性能测试

直接上对比图

上图为不限制Docker 容器大小的结果

上图限制了Docker 容器大小为450M

3.3 垃圾回收次数(1000QPS)

上图100 threads 50m Heap 的时候程序直接崩溃了所以为0

第四步:分析

4.1 Docker vs 非 docker

很明显,Docker会带来一定的性能开销,并且随着线程数的增加与QPS的增加,这种开销会更加明显。但是并不是说Docker不好,毕竟性能开销只要多开一个节点就搞定,和Docker带来的便利性相比,几乎可以无视。

4.2 线程数 与 JVM heap

这里的讨论仅限于单核CPU负载较高的运算类API,Serial GC

  • 虽然线程数越多吞吐量越高,但是响应时间会更快的增长
  • Heap过小会导致频繁的垃圾回收(年轻代影响较小,年老代最为突出)甚至会OOM导致程序崩溃
  • Heap过小时,线程数越大,年老代的回收次数显著增多,年轻代反而会降低(年老代回收为主力)
  • Heap过大并不会带来性能提升,但是年轻代回收次数会显著减少,而年老代几乎不受影响

4.3 一句话结论

对于跑在单核CPU上的运算类API, 根据业务需求(最大响应时间)来调试找到最大线程数,然后依据线程数调试出heap大小(主要看年老代的回收次数)

4.4 回到我的开源项目

很简单,10 threads 100m heap

原文转自:https://juejin.im/post/5d4cd38cf265da03b120394c