Jmeter压测,BeanShell内存溢出问题的排查及解决


测试场景

需要使用Jmeter对Go语言实现的后端服务执行阶梯递增式压测,每阶梯增加2000线程,每个阶梯维持1小时,直至加压到10000线程。

每秒发送1次请求,每次请求前需要使用到BeanShell PreProcessor获取实时时间,生成动态signature,添加到HTTP请求头中。开启HTTP请求的keep-alive,设置客户端实现方式为HttpClient4。

准备1台8核16G的Linux实例作为控制节点,10台8核16G的Linux实例作为工作节点(jmeter:5.4.1,jdk:1.8.0_291)

Linux及Jmeter配置优化

1、向/etc/sysctl.conf增加以下内容后,执行sysctl -p /etc/sysctl.conf,优化内核相关参数

    net.ipv4.ip_forward = 1#开启路由功能

    net.ipv4.conf.default.rp_filter = 1#禁用所有IP源路由

    net.ipv4.conf.default.accept_source_route = 0#禁用icmp源路由选项

    kernel.sysrq = 0#关闭SysRq功能,SysRq代表的是Magic System Request Key

    kernel.core_uses_pid = 1  #控制core文件的文件名是否添加pid作为扩展

    net.ipv4.tcp_syncookies = 1# tcp syncookie,默认关闭

    kernel.msgmnb = 65536 #默认的每个消息队列的最大尺寸(byte),默认为16384

    kernel.msgmax = 65536#消息队列中单条消息的最大尺寸(byte),默认8192

    kernel.shmmax = 68719476736#共享内存中的最大内存块尺寸(byte),默认33554432(32M),这里是65536M

    kernel.shmall = 4294967296  #kernel.shmall的单位是页面数,当前的x86体系上这个单位是4K,这里是2048G的共享内存总量,默认2097152

    fs.file-max = 6553600  #系统级最大打开文件数,还要结合limits.conf的soft和hard限制

    net.ipv4.tcp_max_tw_buckets = 5000#1st低于此值,TCP没有内存压力,2nd进入内存压力阶段,3rdTCP拒绝分配socket(单位:内存页)

    net.ipv4.tcp_sack = 1#定义SYN重试次数

    net.ipv4.tcp_window_scaling = 1 #开启窗口缩放功能

    net.ipv4.tcp_rmem = 4096  87380  4194304  #接受缓冲的大小:MIN,DEFAULT,MAX

    net.ipv4.tcp_wmem = 409616384  4194304  #socket的发送缓存区分配的MIN,DEFAULT,MAX

    net.ipv4.tcp_max_syn_backlog = 8192#syn队列,默认1024,1280可能工作不稳定,需要修改内核源码参数

    net.core.netdev_max_backlog = 32768#进入包的最大设备队列.默认是300,对重负载服务器而言,该值太低,可调整到2000.

    net.core.somaxconn = 32768  #listen()的默认参数,挂起请求的最大数量.默认是128.对繁忙的服务器,增加该值有助于网络性能

    net.core.wmem_default = 8388608#表示套接字发送缓冲区大小的缺省值,会覆盖net.ipv4.tcp_wmem的DEFAUL值

    net.core.rmem_default = 8388608  #表示套接字接收缓冲区大小的缺省值

    net.core.rmem_max = 16777216  #表示套接字接收缓冲区大小的最大值

    net.core.wmem_max = 16777216  #表示套接字发送缓冲区大小的最大值,会覆盖net.ipv4.tcp_wmem的MAX值

    net.ipv4.tcp_timestamps = 0  #禁用时间戳,时间戳可以避免序列号的卷绕

    net.ipv4.tcp_synack_retries = 2  #syn-ack握手状态重试次数,默认5,遭受syn-flood攻击时改为1或2

    net.ipv4.tcp_syn_retries = 2  #外向syn握手重试次数,默认4

    net.ipv4.tcp_tw_recycle = 1  #开启 TCP 连接中 TIME-WAIT sockets 的快速回收,默认为 0 ,表示关闭。

    net.ipv4.tcp_tw_reuse = 1#开启重用。允许将 TIME-WAIT sockets 重新用于新的 TCP 连接,默认为 0 ,表示关闭;

    net.ipv4.tcp_mem = 94500000 915000000 927000000  #1低于此值,TCP没有内存压力,2在此值下,进入内存压力阶段,3高于此值,TCP拒绝分配socket.上述内存单位是页

    net.ipv4.tcp_max_orphans = 3276800 #选项用于设定系统中最多有多少个TCP套接字不被关联到任何一个用户文件句柄上,如果超过这个数字,孤立连接将立即被复位并打印出警告信息

    net.ipv4.tcp_fin_timeout = 30  #修改系統默认的 TIMEOUT 时间

    net.ipv4.tcp_keepalive_time = 300  #表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为5分钟。

    net.ipv4.ip_local_port_range = 102465000#表示用于向外连接的端口范围。缺省情况下过窄:32768到61000,改为1024到65535。

    net.ipv4.ip_conntrack_max = 655360#增大iptables状态跟踪表

    net.ipv4.netfilter.ip_conntrack_tcp_timeout_established = 180#设置默认 TCP 连接时长为180秒

2、向/etc/security/limits.conf增加以下内容

*soft nofile 65535

*hard nofile 65535

再向/etc/profile增加以下内容,执行source /etc/profile,持久优化文件句柄

ulimit -n 65535

4、jmeter.properties配置设置

httpclient.reset_state_on_thread_group_iteration=false #线程组每次循环是否重置连接状态

client.tries=3 #初始化远程工作节点重试次数

client.retries_delay=3000 #初始化远程工作节点超时时间

httpclient4.retrycount=1 #请求重试次数

httpclient4.idletimeout=60000 #连接空闲时间

httpclient4.time_to_live=60000 #连接保持时间

5、bin/jmeter文件,JVM优化

: "${HEAP:="-Xms12g -Xmx12g -Xss512k -XX:MaxMetaspaceSize=1g"}"

: "${GC_ALGO:="-XX:+UseG1GC -XX:+DisableExplicitGC -XX:MaxGCPauseMillis=100 -XX:G1ReservePercent=20 -XX:+UseStringDeduplication -XX:ConcGCThreads=2"}"

问题现象

远程调用执行,持续40分钟以后,设置的12GB堆空间不足,jmeter发送的请求量大幅下降。

jmeter脚本

使用ps -ef|grep java|awk '{printf $2}'|head -1|xargs -I {} jstat -gc {} 2000,查看JVM的GC状态。可查看到大量老年代资源累积,最终触发FullGC,而后不断频繁触发FullGC。


GC信息

排查问题

1、使用jmap -dump:live,format=b,file=heapLive.hprof ${jmeter_pid}命令,获取堆快照文件

2、将文件下载到本地后,执行jvisualvm命令,打开jvisualvm,并加载堆快照文件,分析堆空间使用信息


heap信息

通过分析堆快照,我们可以看到,堆空间的使用,主要被bsh类下的实例及String类型使用

通过堆快照分析,我们定位出了Jmeter的性能问题主要出现在Beanshell生成signature给http请求使用这块。首先,我们先禁用掉BeanShell PreProcessor组件,直接以每台1000线程(共计10000线程)的并发量运行,看内存溢出问题是否消失,来验证我们的猜想。

禁用掉BeanShell PreProcessor组件后,以每台1000线程(共计10000线程)的并发量运行,最终看到,工作节点的内存最终维持到了8GB左右,并可持续运行40min以上,证明了我们的猜想是正确的

所以我这边,首先是对BeanShell中的代码进行了优化,使用StringBuilder拼接字符串,将signature生成并put后,设置所有new出的对象为null。启用BeanShell PreProcessor组件,而后再次以每台1000线程(共计10000线程)的并发量运行,工作节点最终仍然出现了内存溢出的问题,说明存在内存溢出的地方可能并不是beanshell的代码。

4、网上查阅资料,最终确定是Jmeter的BeanShell组件存在内存溢出的问题,官方文档的说明如下:


beanshell

在长时间运行的Jmeter脚本中,使用BeanShell组件,会占用大量内存;如果需要长时间运行,则需要设置BeanShell组件中reset Interpreter选项为True。

但是通过设置BeanShell组件中reset Interpreter选项为True,我们再次运行Jmeter测试,会发现虽然内存增加的问题得到了较大的改善,但测试的吞吐量无法达到期望效果,这显然也是我无法接受的。

通过jstack -F ${jmeter_pid}命令,发现大量的线程处于阻塞状态,通过阅读Jmeter源码发现解释器重置,每次都需要重新执行java.lang.ClassLoader.loadClass方法,该方法存在一个synchronized同步块阻塞了线程,从而导致吞吐率无法提高

BeanShellServer
loadClass

问题解决

由于BeanShell组件存在内存溢出的问题,且在设置reset Interpreter选项为True后,吞吐量会被限制,我不得不选择放弃使用BeanShell来实现生成signature的功能。

目前可供我使用的方案有两种:

1、使用官方支持的Groovy或Jexl3等开发脚本来实现(通过查询网上资料,了解到Groovy虽然内存占用在合理范围内,但和BeanShell存在相同的类加载引发的线程阻塞问题,导致吞吐率很低

2、使用Jmeter自定义函数(我目前使用的是这一种方式,经测试能有效的避免内存溢出问题,并且不会存在线程阻塞导致吞吐量不达标的问题

关于如何实现Jmeter自定义函数,详见文章:https://www.jianshu.com/p/37f1e8329fc7

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容