后台使用java程序,通过springboot集成的stomp协议暴露websocket接口,所以下文测试过程会有特定的stomp报文,无需在意,关注流程即可
本次测试使用jmeter模拟大量用户接收群消息的场景,可覆盖连接数以及消息并发的压测

一、jmeter下载安装

下载地址
https://jmeter.apache.org/download_jmeter.cgi
在这里插入图片描述
下载zip安装包到本地解压
在这里插入图片描述
进入/bin目录,执行jmeter.bat启动
在这里插入图片描述
打开一个命令行窗口和一个GUI窗口,启动成功
在这里插入图片描述
在这里插入图片描述
可以通过Options -> Choose Language -> Chinese切换语言为中文
在这里插入图片描述

二、安装JMeter WebSocket Samplers 插件

jmeter默认不支持websocket协议,需要安装插件

下载地址
https://bitbucket.org/pjtr/jmeter-websocket-samplers/downloads/?spm=a2c4g.11186623.0.0.6cfd2486vZsEcu
在这里插入图片描述
下载jar包后,复制到jmeter目录的lib/ext下
在这里插入图片描述
重启jmeter,添加一个线程组
Test Plan右键 -> 添加 -> 线程(用户) -> 线程组
在这里插入图片描述
然后线程组右键 -> 添加 -> 取样器,可以看到websocket的组件,则插件安装成功
在这里插入图片描述

三、通过postman测试websocket连接

在开始jmeter压测前,先通过postman测试两个连接的群发消息功能正常

1、新建id为1002用户的websocket连接

输入后台握手地址,其中1002是sessionId,实际会由客户端随机生成
点击Connect
在这里插入图片描述
可以看到握手成功,后台响应了一个 o,此时已经完成协议升级
在这里插入图片描述
然后输入以下stomp指令,点击send
[“CONNECT\nbusinessId:5\nmemberId:1002\naccept-version:1.1,1.0\nheart-beat:10000,10000\n\n\u0000”,“SUBSCRIBE\nbusinessId:5\nmemberId:1002\nid:sub-1\ndestination:/topic/12344321\n\n\u0000”]

这个指令是stomp协议的内容,可以不用关注,简单理解为向后台发送一条消息,订阅了一个名为12344321的队列
在这里插入图片描述发送成功,服务端针对CONNECT指令响应了a[“CONNECTED\nversion:1.1\nheart-beat:0,0\nuser-name:1002\n\n\u0000”]
在这里插入图片描述

2、新建id为1003用户的websocket连接

重复上述步骤,把地址栏以及stomp指令里的1002改成1003
在这里插入图片描述

3、通过1002用户发送群消息

在1002的连接上输入以下指令,向群组12344321发送一条消息

[“SEND\nbusinessId:5\nmemberId:1002\ndestination:/websocket/sendToGroup\ncontent-length:110\n\n{“senderId”:“1002”,“receiverId”:“12344321”,“messageContent”:“hello everyone”,“messageType”:1,“receiverType”:2}\u0000”]
在这里插入图片描述发送后,由于1002自身也订阅了群组12344321,所以他也收到了服务端的推送
在这里插入图片描述打开1003的窗口,同样收到了群消息
在这里插入图片描述为方便测试,后台提供了接口,查看websocket连接数以及对应的IP和端口
在这里插入图片描述
在这里插入图片描述
通过本地cmd执行netstat命令,可以查看到这两个端口在使用中
在这里插入图片描述
websocket后端工作正常,接下来可以着手开始用jmeter压测

四、jmeter压测

先观察下插件提供的几个Websocket测试组件
在这里插入图片描述
Open Connection用于开启一个websocket连接,会完成协议升级
Single Write 用于向服务端发送数据,看名字,Single,只能发送一次,如果需要发送多次,可以建多个WebSocket Single Write Sampler
Single Read 用于接收服务端的推送,同样只能接收一次,如果要接收多次消息,要建多个
request-response 一个发送以及一个接收,相当于Write + Read

jmeter压测后可以保持连接活跃,但并不能像聊天窗口一样随时发送和接收消息,所以需要针对自己的测试场景组合这几个组件

新建计数器

先创建一个计数器,用以模拟不同用户建立连接
线程组右键 -> 添加 -> 配置元件 -> 计数器
在这里插入图片描述
设置计数器从2000开始,每次递增1,引用名称填sessionId,这个相当于变量名,后续可以用在请求地址或者请求参数里
在这里插入图片描述

新建WebSocket Open Connection

用于握手升级协议

线程组右键 -> 添加 -> 取样器 -> Websocket Open Connection
在这里插入图片描述
在新建的Websocket Open Connection中填入后台协议升级的接口IP、端口以及路径地址
在这里插入图片描述其中路径里使用了变量${sessionId}

新建Websocket Single Write

在这里插入图片描述
在Connection里选中use existing connection,也就是会使用上面WebSocket Open Connection组件建立的连接

然后在Request data里填入stomp指令,完成用户身份绑定,以及订阅群组12344321

[“CONNECT\nbusinessId:5\nmemberId:KaTeX parse error: Undefined control sequence: \naccept at position 12: {sessionId}\̲n̲a̲c̲c̲e̲p̲t̲-version:1.1,1.…{sessionId}\nid:sub-1\ndestination:/topic/12344321\n\n\u0000”]
在这里插入图片描述

新建WebSocket Single Read

在这里插入图片描述
通过之前的postman测试可以发现,在握手成功以及CONNECT指令发送成功后,服务端都会有一个响应

(实际上spring内置的stomp集成,服务端会针对每一个客户端连接启动一个类似心跳的任务,每隔25秒推送一个报文h,这里为了方便测试,修改源码把这个任务停掉了,如果你的websocket服务端也有类似的定时推送,测试过程中需要留意,因为每个Read Sampler只能接收一次消息)
在这里插入图片描述
所以我们这里要建立3个Read Sampler组件,前2个用于接收服务端的响应,第3个用于等待接收群消息
Read Sampler的Connection同样要选择 use existing connection
在这里插入图片描述
为了方便测试,把这个3个Read Sampler名字后面分别加上 s e s s i o n I d − 握手、 {sessionId}-握手、 sessionId握手、{sessionId}-订阅群组、${sessionId}-群消息

其中第3个Read Sampler由于要接收消息,将它的Response Timeout调大点
在这里插入图片描述

添加结果监听器

线程组右键 -> 添加 -> 监听器 -> 查看结果树
在这里插入图片描述

调整线程数

线程数调整为12000
在这里插入图片描述

执行测试

在这里插入图片描述
执行完后,结果里目前只有握手和订阅群组的响应,群消息还没发,所以第3个Read Sampler在等待中
在这里插入图片描述
通过后台接口查看连接数,包括了jmeter的12000个连接以及postman的2个连接
在这里插入图片描述
端口号
在这里插入图片描述
然后通过postman的用户1002再次发布群消息
在这里插入图片描述

jmeter里第3个Reader收到了消息
在这里插入图片描述单机能建立的连接数受端口号限制,jmeter建立连接使用的端口号大多数从49000开始,到最大端口号65535,再加上其他进程占用的端口,瓶颈大概在16000

比如把线程数调整到20000,执行到16000就开始增长缓慢甚至卡住了,jmeter也有报错无连接可以复用
所以如果需要对服务器做连接数上限等的压测,比如数十万连接,就需要多台服务器配合了
在这里插入图片描述

踩坑记录

通过上面最后一张图,可以看到jmeter其实会复用一些连接,如果只通过WebSocket Open Connection来试图测试连接上限,会发现后台连接会短暂达到线程组的指定数字,但很快会降低到一个随机值,期间没有日志,但多测几次后,又有可能所有连接都保持活跃,线程组的线程数越多,这个现象越容易出现

通过对stomp的代码排查,发现部分连接被close掉的过程中,有一个异常被catch掉了,修改源码后抛出,可以在控制台看到一个EOF Exception
在这里插入图片描述
这个异常通常出现在读取的过程中意外遇到输入流末尾导致,放在这个场景里,就是jmeter在某个端口建立了连接A,后来又关闭A,在同一个端口上建立了连接B,在压测的过程中,这个变化非常快,服务端还没来得及做清理,于是在服务端看来,A和B都是某个IP的特定端口,是同一个socket连接,所以读取数据的时候就发生了混乱

至于为什么jmeter会复用这个端口,我的理解是jmeter的几个websocket组件,不管是Single Read Sampler还是Open Connection,其实都是一次性的,并不能持续的接收或者发送消息,那么当其执行完毕,jmeter认为其使命已经结束,在资源紧张的情况下,可以收回该端口了

通过单纯的OpenConnection测试,发现jmeter日志里确实有标明某线程已经Done并且Finished了
在这里插入图片描述
而通过本文测试接收群消息的方式,第3个Read Sampler的timeout时间很长,在群消息到来前,这个线程的任务并没有结束,所以也就不能清理并复用其端口了,于是所有的连接都通过不同的端口建立

点赞(1) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部