记一次 JMeter 压测 HTTPS 性能问题

网站建设5年前发布
69 0 0

在使用 JMeter 压测时,发现同一后端服务,在单机 500 并发下,HTTP 和 HTTPS 协议压测 RT 差距非常大。同时观测后端服务各监控指标水位都很低,因此怀疑性能瓶颈在 JMeter 施压客户端。,切入点:垃圾回收,首先在施压机观察到 CPU 使用率和内存使用率都很高,详细看下各线程 CPU、内存使用情况:,发现进程的 CPU 使用率将近打满,其中 GC 线程 CPU 使用率很高,记一次 JMeter 压测 HTTPS 性能问题,再看下 gc 的频率和耗时,发现每秒都有 YoungGC,且累计耗时比较长,因此先从频繁 GC 入手,定位问题。,记一次 JMeter 压测 HTTPS 性能问题,在压测过程中,对 JMeter 的运行进程做了 HeapDump 后,分析下堆内存:,记一次 JMeter 压测 HTTPS 性能问题,可以看到 cacheMap 对象占用了 93.3%的内存,而它又被 SSLSessionContextImpl 类引用,分析下源码,可以看出,每个 SSLSessionContextImpl 对象构造时,都会初始化 sessionHostPortCache 和 sessionCache 两个软引用 Cache。因为是软引用,所以在内存不足时 JVM 才会回收此类对象。,通过上述代码,发现 sessionCache 和 sessionHostPortCache 缓存默认大小是 DEFAULT_MAX_CACHE_SIZE,也就是 20480。对于我们压测的场景来说,如果每次请求重新建立连接,那么就根本不需要这块缓存。再看下代码逻辑,发现其实可以通过
javax.net.ssl.sessionCacheSize 来设置缓存的大小,在 JMeter 启动时,添加 JVM 参数-Djavax.net.ssl.sessionCacheSize=1,将缓存大小设置为 1,重新压测验证,观察 GC。
,记一次 JMeter 压测 HTTPS 性能问题,可以看出,YGC 明显变少了,从 1 秒 1 次,变成了 5-6 秒 1 次。那么观察下压测的 RT,结果。。。竟然还是 1800ms,本来 100ms 的服务被压成 1800ms,看来问题不在于 SSLSession 的缓存。再回到 GC 的耗时分析部分,仔细看下,其实 Full GC 只有 1 次,阻塞性的耗时并不多,Young GC 虽然频繁,但阻塞时间很短,也不至于将 SSL 加解密的 CPU 计算时间片全部抢占。看起来压力就是单纯的 SSL 握手次数多,造成了性能瓶颈。,调整思路:为什么频繁 SSL 握手,回到问题背景,我们是在做压力测试,单机会跑很高的并发模拟用户量,出于性能考虑,完全可以一次握手后共享 SSL 连接,后续不再握手,为什么 JMeter 会如此频繁握手呢?,带着这个问题,看了下 JMeter 官方文档,果然有惊喜!,记一次 JMeter 压测 HTTPS 性能问题,原来 JMeter 有 2 个开关在控制是否重置 SSL 上下文的选项,首先是
https.sessioncontext.shared 控制是否全局共享同一个 SSLContext,如果设为 true,则各线程共享同一个 SSL 上下文,这样对施压机性能压力最低,但不能模拟真实多用户 SSL 握手的情况。
,第二个开关
httpclient.reset_state_on_thread_group_iteration 是线程组每次循环是否重置 SSL 上下文,5.0 之后默认为true,也就是说每次循环都会重置 SSL 上下文,看来这就是导致 SSL 频繁握手的原因。
,回归测试,在 jmeter.properties 中将配置每个线程循环时,不重置 SSL 上下文,在 PTS 控制台再次启动压测,RT 直接下降 10 倍。,记一次 JMeter 压测 HTTPS 性能问题,修改前,记一次 JMeter 压测 HTTPS 性能问题,修改后,源码验证,下面从源码层面分析下 JMeter 是怎么实现循环重置 SSL 上下文的,代码如下:,在每次基于 Apache HTTPClient4 的 HTTP 采样器执行时,都会调用 resetStateIfNeeded 方法,在进入方法时读取httpclient.reset_state_on_thread_group_iteration 配置,即 resetStateOnThreadGroupIteration。如果是 true,重置当前线程的连接池状态、重置 SSL 上下文,然后再将 resetStateOnThreadGroupIteration 置为 false。,因为 JMeter 的并发是基于线程实现的,resetStateOnThreadGroupIteration 这个开关放在 ThreadLocal 里,在每次循环开始时,会调用 notifyFirstSampleAfterLoopRestart 方法,重置开关,运行一次后,强制把开关置为 false。这保证了每次循环只有第一个采样器进入此逻辑,也就是每次循环只执行一次。,本次解决了 JMeter5.0 版本以上压测 HTTPS 协议的性能问题,经验总结如下:

© 版权声明

相关文章