ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

JMeter WebSocket接口测试全攻略:从环境搭建到压测实战

JMeter WebSocket接口测试全攻略:从环境搭建到压测实战 1. 项目概述WebSocket接口测试到底卡在哪做接口测试这些年HTTP接口那一套大家都熟——Postman发请求、JMeter加线程组、断言状态码和响应体套路固定坑也踩得差不多。但一遇到WebSocket接口很多团队直接懵了拿Postman调试半天连不上用JMeter普通的HTTP Sampler更是无从下手。这不是工具不够好而是WebSocket和HTTP压根不是一回事。WebSocket最核心的特点是长连接和全双工通信。HTTP是“你问一句、我答一句”服务端没法主动往客户端推数据WebSocket则是建立连接后双方随时都能发消息服务端可以主动推送实时数据。生活里打个比方HTTP就像寄信一封信一个来回对方不说你也不知道他收到没有WebSocket更像打电话线路一直通着两边随时能说话。这个特性决定了测试方式完全不同——你需要一个能维护长连接、能发消息、能等推送、还能验证内容的工具。本项目要解决的就是用JMeter完成WebSocket接口的功能测试与性能压测。我在实际项目里用这套方案测过实时行情推送、在线客服消息、iot设备状态上报等场景功能测试能替代大半Postman的活儿压测又能直接给服务端上压力。适合刚接触WebSocket接口测试的功能测试工程师、测试开发也适合后端开发自测接口。这篇内容从环境搭建讲到常见报错再讲到压测场景设计尽量把坑都替你趟一遍。2. 核心思路与技术方案选型2.1 WebSocket接口测试的技术难点在哪里接口测试通常要覆盖三块连接是否成功、消息交互是否正确、服务端主动推送能否收到且不乱序。HTTP接口测试天然覆盖前两块因为请求和响应是绑定的但WebSocket的场景尤其是服务端主动推送普通的接口测试工具根本无能为力。还有一个容易被忽略的难点连接状态管理。HTTP每个请求都是独立的不存在“连接断开”这个概念WebSocket则不同建立连接后如果客户端长时间不发消息服务端可能因为空闲超时主动断开也可能因为中间网络抖动导致连接假死。这些状态变化如果不处理测试脚本跑到一半就断结果全是报错。这也就是为什么搜索WebSocket测试时很多人遇到“websocket closed by server before response”这类报错——后面我会专门讲。另一个痛点是消息顺序和关联。WebSocket是双向的客户端发一条服务端可能回一条也可能回多条还可能过一会儿再推一条。普通HTTP测试里“请求—响应—断言”的线性模式不适用了你得让脚本具备“等”的能力也就是设置读取超时循环接收消息按业务逻辑判断这条消息是不是自己要的。这些能力JMeter基础组件并不自带需要专门的WebSocket Sampler扩展。2.2 为什么用JMeter而不是Postman或自写脚本我见过不少团队用Postman调试WebSocket接口Postman其实支持WebSocket请求简单调试确实方便。但一到性能测试就抓瞎——Postman本质是调试工具不是压测工具没法模拟几百上千并发连接也没法聚合统计TPS、响应时间等指标。自写脚本Node.js、Python灵活性倒是高但开发和维护成本摆在那里对不会写代码的测试同学不友好。JMeter的优势在于它既是功能测试工具又是性能测试工具生态成熟社区插件也多。加上WebSocket Sampler这个插件之后JMeter能够覆盖WebSocket测试的完整链路建立连接、发送消息、接收消息、断言校验、参数化、并发压测。而且JMeter的测试脚本可以复用功能测试脚本调调线程数就能变成压测脚本这对日常迭代效率提升很明显。选型的时候也要注意版本兼容。我用的方案是JMeter 5.x JDK 8/11WebSocket Sampler插件在JMeter 5.6.2上实测没问题3.x老版本就别折腾了升级JMeter比折腾兼容性省事得多。2.3 测试场景盘点连接、交互、推送、压测WebSocket接口测试从场景上可以拆成四类每一类的侧重点不一样连接场景验证握手是否成功、wss证书是否正确、连接超时时间设置是否合理。这个一般在功能测试阶段就能覆盖。交互场景客户端发一条消息服务端返回正确的业务数据。重点在于请求数据格式、响应数据断言、消息关联比如拿响应里的id再发下一条请求。推送场景服务端主动向客户端推数据客户端被动接收。重点在于连接要一直保持接收脚本要一直监听不能漏消息、不能乱序。压测场景模拟大量客户端同时建立连接、发送消息、维持连接、接收推送验证服务端的承载能力和稳定性。前两类用JMeter简单配置就能完成第三类推送场景需要单独设计读取逻辑第四类压测要特别注意连接数的设计这些后面都会展开讲。3. 环境准备从零搭建JMeter WebSocket测试环境3.1 JMeter安装与版本选择官方下载页面提供源码包和二进制包Windows直接下载zip包解压macOS/Linux下载tgz包。解压后进入bin目录Windows运行jmeter.batLinux/macOS运行jmeter.sh就弹出来图形界面了。有一个前置条件要注意JMeter是Java应用需要先装JDK。JMeter 5.4以上版本要求JDK 8及以上建议直接用JDK 11实测最稳。装完JDK后可以在命令行执行java -version确认版本看到类似openjdk version 11.0.x就OK。如果环境变量没配好JMeter启动会直接报“Unable to find a Java JDK”的错误这时候去检查JAVA_HOME。安装完建议顺手把JMeter的bin目录加到系统PATH里这样命令行直接输jmeter就能启动后续写脚本做命令行压测也方便。另外JMeter默认启动会占用不少内存如果机器内存紧张可以编辑bin目录下jmeter.bat/jmeter.sh里的堆内存参数通常-Xmx1g改成-Xmx512m能让老机器跑得动不过压测机建议内存8G以上别省这点内存。3.2 安装WebSocket Sampler插件这是整个环境准备里最关键的步骤。JMeter原生不带WebSocket协议支持需要安装第三方插件。我用的是由Peter Doornbosch维护的JMeter WebSocket Samplers插件开源免费持续更新支持建立连接、发送消息、读取消息、关闭连接、二进制消息等完整能力。推荐用JMeter Plugins Manager安装这是最省事的方式。先在官网下载Plugins Manager的jar包放到JMeter的lib/ext目录下重启JMeter菜单栏会出现“Plugins Manager”选项。打开后在Available Plugins列表里搜“WebSocket”勾选“WebSocket Samplers by Peter Doornbosch”点击Apply按钮插件会自动下载并安装再次重启就生效了。如果网络环境不方便用Plugins Manager也可以手动下载插件的jar包放到lib/ext目录但要特别注意版本匹配插件jar包的版本号和JMeter版本不一致时会出现加载失败的报错日志里能搜到ClassNotFoundException这个问题我在2.x版本上遇到过一次换成匹配版本就好了。安装完成后右键测试计划添加Sampler如果能找到“WebSocket Open Connection”“WebSocket Request-Response Sampler”等选项说明装成功了。3.3 搭建第一个WebSocket测试计划先搭一个最小可用的测试计划确认工具链路通了再逐步加复杂度。测试计划的结构是这样的Test PlanThread GroupWebSocket Open ConnectionWebSocket Request-Response SamplerWebSocket CloseView Results TreeThread Group默认线程数1、循环次数1就够先跑通再说。View Results Tree放在最后用来查看每个Sampler的请求数据和响应数据。这其实和HTTP接口测试的思路是一致的只是把HTTP Sampler换成了WebSocket的Sampler多了一个Open Connection和Close的步骤。调试阶段建议只开一个线程因为WebSocket连接是有状态的多线程并发调试时界面上的信息会很乱不好定位问题。后面压测再改成多线程。4. 核心实操从握手到实时消息验证4.1 WebSocket Open Connection建立连接的关键参数第一个要配置的是连接Sampler。在Thread Group下右键→添加→Sampler→WebSocket Open Connection打开配置界面。里面几个关键参数Server Name or IP目标服务器地址填域名或IP。Port服务端监听端口。WebSocket默认是ws的80端口wss的443端口如果服务端自定义端口就填实际端口。Protocol选择ws还是wss加密连接选wss。PathWebSocket的路径比如/ws/chat。这个一般由服务端接口文档给出。Connection timeout连接超时单位毫秒。建议设3000-5000太短容易误报超时太长出错时定位问题很慢。连接成功后这个Sampler会生成一个Connection ID后续的发送消息、关闭连接Sampler都要通过这个ID来指定操作哪条连接。多线程场景下每个线程有自己独立的Connection ID互不干扰这是WebSocket测试和HTTP测试差异比较大的地方。wss协议有个证书的问题。如果服务端用的是自签名证书JMeter默认会校验证书连接会直接报错。解决办法是把服务端证书导入JMeter使用的JDK的cacerts信任库或者用Plugins Manager安装JMeter的“Install Certificates”工具。实测下来导入证书最省心具体命令格式是keytool -import -alias 别名 -keystore cacerts -file 证书文件执行时会提示输入密码默认是changeit。4.2 发送消息与接收响应WebSocket Request-Response Sampler连接建立之后就到了最核心的请求Sampler——WebSocket Request-Response Sampler。这个Sampler做的事情是在指定的WebSocket连接上发送一条消息然后等待接收一条响应消息。它适合处理“发一条、收一条”的交互场景类似HTTP的单个请求-响应只是跑在长连接上面。配置上要关注这几个字段Connection ID选择在哪个连接上发送选“Use existing connection”并指定前面Open Connection产生的连接ID。Request Data要发送的消息体服务端接口文档定义什么格式就填什么格式。常见的是JSON字符串比如{type:ping,data:hello}。Message Type文本消息选Text二进制数据选Binary。绝大多数业务接口都是文本游戏或者是文件传输场景才用二进制。Read timeout读取响应的超时时间。这个参数很重要设太短服务端稍微慢一点就会误报设太长服务端真的没返回时脚本会卡很久。我一般设5000业务慢的接口设10000。发送和接收在这个Sampler里是绑定的发完必须等到一条响应才算成功。但我实际使用中发现这个Sampler存在一个容易误导人的地方如果服务端在你发消息之前就主动推送了一条消息就可能会读到旧消息导致响应对不上。遇到这种情况要拆开用“WebSocket Sampler”发送消息再用单独的“WebSocket Single Read Sampler”来读响应把发送和接收解耦。这也是WebSocket测试和HTTP测试底层思维方式的一个不同点。4.3 处理服务端主动推送用Single Read Sampler做持续监听服务端主动推送是WebSocket的招牌能力也是最难测的部分。比如行情推送服务服务端每隔几秒推一次最新价格再比如在线客服客服回复的消息不需要客户端轮询服务端直接推过来。这种情况下如果用Request-Response Sampler你会发现脚本总是读不到数据因为服务端根本不会因为客户端发消息才回它自己就在推。处理方案是用“WebSocket Single Read Sampler”。这个Sampler只有一个动作在当前连接上等待并读取一条消息读到就结束超时则报错。把它放在线程组里配合循环就能实现持续接收服务端推送的效果。具体操作思路Open Connection建立连接。添加一个While Controller循环条件设为任务没结束比如可以用一个自定义变量continue控制。在While Controller里放一个WebSocket Single Read Sampler读取超时设置为服务端推送间隔的2倍左右。读取到的消息存入变量然后通过断言校验数据是否符合预期。实际测试中我发现一个坑服务端推送频率很高时Single Read Sampler每次只读一条循环次数不够就会漏消息。解决方法是每次读取成功后把读取到的消息追加到一个CSV文件或者记录到日志里而不是只保留最后一条。这样即使UI上只显示了最后一条完整数据也已经落盘后续可以离线分析消息是否有遗漏、乱序。这个思路在后面压测场景里同样适用。4.4 接口测试的核心断言与关联功能测试的目的是验证接口返回的数据在业务上对不对所以断言不能少。JMeter的断言对WebSocket响应同样适用。我在WebSocket测试中最常用的断言方式是给Sampler添加“Response Assertion”然后配置“响应文本”包含某些关键词。举个例子连接一个股票行情服务发送订阅请求后服务端推回来的消息格式是{symbol:AAPL,price:188.23,time:...}那断言就设置模式匹配symbol:AAPL以及price:188.2开头这样能快速确认推回来的是否是正确合约的正确价格。响应数据里如果有关联字段比如push消息里带了一个sequence序号后续请求要带上这个序号才能继续这就需要提取和关联。JMeter里可以用“JSON Extractor”从JSON响应里提取值存入变量然后下一个请求的Request Data里用${变量名}引用。提一个实际踩过的坑从WebSocket响应里提取数据时因为响应文本是附加了额外包装的JSON Extractor的路径有时候不好使优先用正则表达式提取更稳定比如用sequence:([^])这种模式。4.5 参数化多用户、多场景数据驱动接口测试到了多用户并发阶段参数化就变成刚需。WebSocket场景下参数化主要做两件事一是用不同用户身份连接二是发送不同的请求数据。最方便的是CSV Data Set Config组件在测试计划里准备一个CSV文件每行是一个用户的token或roomId之类的参数线程组里的线程数设置得和CSV行数一致每个线程自动读取一行数据。这个方式我在HTTP测试里用得很熟WebSocket测试同理只是要注意连接建立的时候就要把CSV里的token带进去比如在Open Connection的Request Data里加上登录授权信息或者在路径上拼上参数。参数化有一个常见的误区就是CSV文件里只有一两行数据但线程数设置了几百导致大量线程重复用同一身份连接服务端一查发现同一账号登录了几百次直接拒绝或者踢下线压测数据全废。做WebSocket压测时账号资源要提前准备好一个线程对应一个账号至少做到一个线程循环时能用不同账号就最好。5. 常见报错与排查实操5.1 “websocket closed by server before response”报错分析这个报错是搜索热词里出现频率很高的一个报错信息类似stream disconnected before completion: websocket closed by server before response从字面就看得出来服务端在客户端收到响应之前就把连接关了。这个报错的常见原因有三个第一是服务端空闲超时。WebSocket长连接不可能一直保持很多服务端会配置空闲超时比如5分钟没消息就自动断开连接。此时客户端还傻傻地等着响应服务端一关客户端就报这个错。定位方法很简单连接建立后别马上操作等个几分钟看连接是否被服务端关闭。解决方法是设计测试脚本时在连接上定期发送心跳消息比如{type:heartbeat}保持连接活跃。第二是服务端处理请求时发现业务异常主动关闭连接。比如认证失败、请求数据格式不对、协议版本不支持等。这种时候要抓服务端日志看关闭时的业务错误码。客户端这边能做的事是把Request Data和响应的错误信息打出来对照接口文档确认数据格式。第三是服务端重启或网络链路中断。这种偶发性比较强排查方式是看服务端重启时间点和客户端报错时间点是否吻合。如果是压测过程中出现的还要考虑服务端是否因为压力过大主动断连比如连接数超过阈值、内存溢出等。5.2 “failed to send websocket request: io...”报错分析这个报错通常和连接断开有关。报错信息形如stream disconnected before completion: failed to send websocket request: io...意思是客户端尝试发送消息时发现底层的连接已经不可用。常见原因有三个连接已经被关闭。可能是服务端关的也可能是客户端脚本里之前的Sampler把连接close了但后面的Sampler还在用同一个Connection ID。这个在调试阶段很容易发生——前一个循环把连接关了下一个循环还在用旧ID发消息。排查时先看连接关闭发生在哪个Sampler把脚本执行顺序理理清楚。网络中间设备超时。有些负载均衡或网关设备会闲置超时清理空闲的WebSocket连接客户端不知道发送时才发现连接没了。这种情况在压测中尤其常见。解决方法同样是心跳机制或者压测时长别拉太长拆成多轮短时间压测。JMeter所在机器连接数达到上限。Linux系统对单进程连接数有默认限制压测时大量长连接占用文件句柄超过限制后新的连接和发送都会失败。这个在高并发压测时很典型后面压测章节我会专门讲怎么查。5.3 握手失败与wss证书问题WebSocket握手本质上就是一次HTTP Upgrade请求握手失败大多是三类问题连接地址不对。协议ws/wss、端口、路径任何一个配错都会失败。建议先在Postman里把地址调通再拿同样的地址填到JMeter里能减少很多无效排查。wss证书不受信任。自签名证书或私有CA证书会导致握手时校验失败。前面讲过用keytool导入证书这地方再补充一个更省事的方案在JMeter的system.properties文件里添加javax.net.ssl.trustStore配置指定信任库路径这样JMeter启动时会直接加载自定义信任库不用动JDK默认的cacerts。服务端要求携带特定Header才能完成握手。有些服务端会在握手阶段校验Origin、Sec-WebSocket-Protocol或Authorization头。JMeter WebSocket插件通常支持配置额外的Header在Open Connection Sampler里可以添加Header属性注意要和服务端文档对齐。5.4 常见问题速查表报错或现象可能原因排查与解决websocket closed by server before response空闲超时、业务异常、服务端重启加心跳、查服务端日志、抓关闭时间点failed to send websocket request: io...连接已断开、网络设备闲置超时、连接数上限检查连接状态、加心跳、调整系统连接数限制handshake失败地址错误、证书不受信任、缺少必填Header先用Postman验证地址、导入证书、补充Header读取超时服务端没有推送、推送间隔大于超时设置确认业务推送逻辑、调大Read timeout响应数据错乱请求-响应解耦没做好、读到了旧消息拆开发送和读取Sampler、用变量标记消息序号压测时大量连接失败账号数量不足、系统连接数限制、服务端限流准备充足账号、调整系统参数、降低并发这张表基本覆盖了WebSocket接口测试中90%的常见报错。遇到没见过的报错我一般的排查路径是查JMeter日志jmeter.log文件里的堆栈信息再从报错堆栈里找关键异常类去插件GitHub的issue里搜大部分问题都能找到答案。6. 压测实战让WebSocket服务扛住真实压力6.1 WebSocket压测场景设计思路WebSocket压测和HTTP压测在思路上有明显差异。HTTP压测关注的是每秒请求数TPS和响应时间WebSocket压测除了这些更要关注连接数、连接维持时间、服务端能同时承载多少长连接。因为每个WebSocket连接都是耗资源的——服务端要为每个连接维持内存状态、编解码缓冲区和线程资源。压测场景设计上要明确几个参数线程数对应最大连接数。比如要测5000在线用户线程数就设5000每个线程建立一个连接。Ramp-up Period表示多长时间内完成所有线程的启动。建议别设太短比如5000线程用300秒起步让服务端逐步建立连接否则瞬间5000个握手请求很容易把服务端打挂。等稳定运行一段时间后再逐步调大。循环次数或持续时间控制压测时长。长连接压测建议用Duration模式通过Scheduler设置持续时间比如持续压15分钟观察服务端是否稳定有没有连接泄漏。还有一个容易忽略的每个线程在建立连接后不只是干等着还得发业务消息比如订阅行情、发送心跳。要设计一个“连接维持 消息发送”的组合逻辑连接保持期间定期发送业务请求并实时接收服务端推送。这样压出来才接近真实业务场景。6.2 压测执行中的系统参数调优压测WebSocket时瓶颈常常不在服务端而在压测机自己。我最常遇到的是测试机端口耗尽和文件句柄耗尽。先看端口耗尽。每建立一个WebSocket连接客户端都要占一个本地端口。默认情况下Linux系统的可用本地端口范围是32768到60999满打满算也就两万多个一旦压测机上的连接数超过这个数新连接就会报错“Cannot assign requested address”。解决方法有两种一是扩大本地端口范围修改/proc/sys/net/ipv4/ip_local_port_range二是用多台压测机分布式压测。再看文件句柄。每个TCP连接都会占用一个文件描述符Linux默认的进程文件句柄限制是1024完全不够用。压测前务必执行ulimit -n 65535调大上限或者写到系统配置里永久生效。我在一次压测中没调这个参数结果连接数到1000左右就开始报错排查了半天才发现是基础环境问题。最后是JMeter自身的堆内存。长连接压测会持续产生响应数据存入内存监听器如果开了大量图表内存很容易爆。压测时建议用命令行模式执行非GUI模式本身内存占用更小再通过-J参数指定堆内存比如jmeter -n -t test.jmx -J-Xmx4g。实测命令行模式比GUI模式稳得多数据也更准。6.3 结果分析与断言压测结束后的数据分析我习惯先看四类指标连接成功率建立连接的成功数/总尝试数理想状态是99%以上。消息发送成功率业务消息发送成功的比例失败说明脚本逻辑或者服务端有问题。请求响应时间重点看平均值、90线、99线。错误率与错误类型抽样看报错信息判断是连接错误还是业务断言失败。用JMeter的“聚合报告”和“查看结果树”就能看前两项“响应时间图”适合看响应时间趋势配合服务端的监控数据CPU、内存、GC、连接数一起分析。这里要提示一点WebSocket压测中连接建立时间和消息响应时间要分开看。连接建立的耗时受网络影响很大如果把它算进业务消息的响应时间里数据会失真。设计脚本时可以把Open Connection单独放一个事务控制器消息收发单独放一个事务控制器计算出更准确的业务耗时。6.4 压测中常见的稳定性和数据完整性问题长时间压测WebSocket我最看重的是三个稳定性指标连接稳定率、消息顺序一致性和数据完整性。连接稳定率是指在压测持续期间已建立的连接是否会被服务端异常断开。曾遇到过压测到第10分钟出现大量“websocket closed by server before response”报错的情况服务端日志显示是空闲连接回收策略误杀了活跃连接。这个问题单靠客户端脚本解决不了反馈给开发调整服务端参数才是正路。但作为测试方你要能把问题量化——在什么时间点、多少连接被断开、对应服务端什么日志——这样开发才能快速定位。消息顺序一致性对实时推送类服务很重要。比如行情服务推送的每一笔数据都带序号客户端收到的序号必须严格递增。压测脚本里我会对收到的每条消息做序号断言记录乱序的数量。这个统计在JMeter里可以用BeanShell或JSR223脚本实现虽然代码不多但能把“消息乱序”这种隐性缺陷暴露出来。数据完整性主要靠采样和落盘。每台压测机把收到的消息追加写入本地文件压测结束后用脚本做校验对比发送端和接收端的消息总量是否一致。有一次我就靠这个发现了推送服务在高峰期会丢消息的问题这在UI上看不出来但数据一对比就露馅了。7. 经验总结与实用建议最后分享几个我在WebSocket接口测试里的实操心得。第一先确认工具链路再研究复杂场景。第一次用JMeter测WebSocket时我上来就写复杂脚本结果连握手都没成功排查了半天发现是插件的jar包版本和JMeter版本不兼容。先用最简单的Open Connection Request-Response Close跑通一个demo后续所有复杂场景都基于这个最小链路去加能少走很多弯路。第二WebSocket测试脚本的调试比HTTP更依赖日志。HTTP请求失败看一眼响应体基本能定位WebSocket连接一断响应体什么都没有只能靠JMeter日志和服务端日志互相印证。建议在脚本里多保存现场信息尤其是连接建立时间、断连时间、最后一条业务消息的内容这些是排查问题的关键线索。第三压测WebSocket时别只盯着TPS这种HTTP常用的指标。WebSocket服务端的核心瓶颈在连接数、内存和连接稳定性。一次压测中TPS很高不代表服务端没问题要看它在持续连接压力下能不能稳住连接断线率是不是一直处于低位。最后再说一个小技巧如果团队里已经有人用Postman调试WebSocket接口别急着丢。先用Postman确认接口地址、消息格式、断连行为再把这些信息填到JMeter里跑自动化调试效率能提升不少。工具之间互相配合比我最初只用JMeter硬调要快得多。
返回列表