背景:
最近投放业务为了优化游戏广告投放效果,要求我们接入腾讯的“一方数据”。功能是开发好了,但经沟通,考虑历史数据也需要上传,好几百万的数据(激活、注册、登录、付费行为)。
一波沟通,这活我接下了。我考虑基于linux服务器,使用jmeter去跑量,接着就发生了下面一系列的“趣事”,且听我娓娓道来。
环境:
linux 系统 8核 16G
已配置 JDK 1.8
已配置 JMeter 环境变量
csv 文件 登录事件 100w行
第一次尝试:
直接100w个线程,3333秒启动完,心想tps大概每秒30个,应该没啥问题,慢慢跑它9个多小时。
结果,跑到3w多线程后,jmeter毫无征兆的结束了。
查看脚本执行期间,云服务器的压力情况,并未出现达到 linux 瓶颈的情况。
查看 jmeter.log 日志,末尾未看到异常信息,但查到了个内存溢出outofMemoryError的信息。
第二次尝试:
调整jmeter配置文件,增加jvm内存。
进入jmeter 的bin目录下,执行
vim jmeter 命令(注意不是 jmeter.sh 文件)
修改HEAP 的值,根据机器的性能设置,这里我调整到8g。
csv文件移除已执行的行数,对应调整线程组的线程数与Ramp-Up时间,最终目的还是以tps 30/s 去请求。
结果,还是跑到3w多线程后,jmeter又毫无征兆的结束了,这下jmeter.log日志内没看到任何error、Exception的关键字了,挠头ing …
排查思路:
1、端口问题?
起初,我怀疑linux端口被占用完了,但百度一查linux有6万多个端口,我只跑到3万就挂了!接着各种搜索,有人说可能是 linux 内核限制了,于是一顿检查。
#在压测时查看服务器和客户机等待的端口数量
netstat -na|grep TIME_WAIT | wc -l
#查看开发的端口范围
cat /proc/sys/net/ipv4/ip_local_port_range
#如果范围很小,则通过echo进行修改
echo “1024 65535” > /proc/sys/net/ipv4/ip_local_port_range
#查看端口释放后的等待时间
cat /proc/sys/net/ipv4/tcp_fin_timeout
#调低端口释放后的等待时间, 默认为60s, 修改为15~30s
echo 30 > /proc/sys/net/ipv4/tcp_fin_timeout
#查看释放的time_wait端口是否可给新连接使用
cat /proc/sys/net/ipv4/tcp_tw_resue
#修改tcp/ip协议配置, 通过配置/proc/sys/net/ipv4/tcp_tw_resue, 默认为0, 修改为1, 释放TIME_WAIT端口给新连接使用。
echo 1 > /proc/sys/net/ipv4/tcp_tw_reuse
#查看tcp/ip协议配置是否支持快速回收socket资源
cat /proc/sys/net/ipv4/tcp_tw_recycle
#修改tcp/ip协议配置,快速回收socket资源, 默认为0, 修改为1.
echo 1 > /proc/sys/net/ipv4/tcp_tw_recycle
#执行:sysctl -p ,使设置立即生效。
sysctl -p
结论:
经过上述查询,发现linux内核都已经完成优化配置了,所以端口占用问题排除了。
2、脚本问题?
接着,我开始反思Http取样器中 KeepAlive 勾选项的作用。
理论情况下,开启KeepAlive选项可以优化性能,大致意思:发起HTTP请求时,需要建立TCP连接,对于普通非keep-alive请求,即不包含请求头Connection: keep-alive的请求,请求完成后,会关闭该TCP连接,再次发起同类请求时,需要再次建立TCP连接,高并发的情况下,会增加服务器资源消耗,对于keep-alive请求,则会告诉服务器,HTTP请求结束后,在条件允许的情况下,保持TCP连接,下次发送请求时,使用上次建立的TCP连接进行数据传输。
会不会是保持了太多TCP连接,导致未及时释放资源,最终导致资源不足,致使 jmeter异常结束呢?去掉 KeepAlive ,再试试就知道了。
第三次尝试:
csv文件再次移除已执行的行数,调整对应线程组的线程数与Ramp-Up时间,Http取样器去掉“KeepAlive”选项,还是以tps 30/s 去请求。
结果:这次跑到2w多次请求就挂掉了,说明也不是这个问题。
排查思路:
这下可以排除 jmeter运存不足、linux 端口不足、jmeter 中 KeppAlive 选项问题。
继续挠头反思,这脚本和之前那些脚本对比,最大的区别就是:量起来了,线程量起来了(线程数)。
经百度查询,Linux线程的最大限制是每个进程的最大限制的一半。在Linux系统里,一个进程可以分配最多64000字节的内存,其中最多可以有32000个线程。(卧槽,怎么这么像我的情况。)
我怀疑jmeter线程释放速度比jmeter启动线程速度慢,最终导致线程达到瓶颈,线程数不足。
第四次尝试:
缩减 jmeter 线程数,采用循环的方式运行。
csv文件再次移除已执行的行数,调整对应线程组的线程数与Ramp-Up时间,Http取样器勾上“KeepAlive”选项,还是以tps 30/s 去请求。
结果: jmeter 脚本终于正常运行了。
总结:
压测线程数过大时,可采用分布式压测,不可盲目觉得线程越多越好,设置过大的线程数。
像我的情况,其实并不是为了并发压测,只为了稳定的跑量,那就可以考虑配合循环次数,结合公式:并发数 =(线程数*循环次数)/ ramp-Up 进行 tps 的设置即可。
至此,本篇踩坑总结结束,吃一垫长一智。
评论区