1. 项目概述为什么要在JMeter里搞双WebSocket连接最近在做一个实时数据大屏的性能压测后端用的是WebSocket协议推数据。一开始我用JMeter的WebSocket Sampler模拟单个用户连接测起来挺顺利。但业务方提了个新需求他们想模拟一个用户同时打开两个不同的数据看板每个看板都通过独立的WebSocket连接接收不同的数据流。换句话说就是要在一个JMeter线程模拟一个用户里同时维持两个活跃的WebSocket连接并且能独立收发消息。这个需求一下就让我有点犯难了因为JMeter的官方WebSocket插件默认设计是“一个取样器管理一个连接”想在一个线程里玩转双WS得动点脑筋。这其实就是典型的“单用户多会话”场景。比如一个交易员同时盯着外汇和股票两个行情窗口或者一个运维人员同时监控服务器CPU和网络流量两个实时图表。如果只用单连接要么得合并数据流增加服务端解析复杂度要么就得开两个JMeter线程但这模拟的是两个用户不符合“一个用户两个标签页”的真实场景。所以实现双WebSocket连接核心目标是在一个虚拟用户VU线程内创建并管理两个完全独立的WebSocket会话让它们能并行工作互不干扰。这对于评估服务端在高并发真实用户场景下的连接管理、消息路由和资源消耗能力至关重要。2. 核心思路与方案选型JMeter实现双WS的几种玩法要实现这个目标首先得拆解JMeter处理WebSocket的机制。目前社区主流用的是WebSocket Samplers插件作者是Maciej Zaleski它提供了WebSocket Open Connection,WebSocket request-response Sampler,WebSocket Ping/Pong,WebSocket Close这几个取样器。每个WebSocket Open Connection取样器在执行时会建立一个WebSocket连接并将这个连接对象Session存储在一个以Connection Name为键的全局映射里。后续的WebSocket request-response Sampler通过指定相同的Connection Name来复用这个连接进行通信。基于这个原理实现双WS连接就有了清晰的路径创建两个具有不同Connection Name的连接并在同一个线程组内妥善管理它们的生命周期。主要有三种实现思路方案一顺序串行独立管理这是最直观的方法。在线程组里先完整执行第一个WS连接的所有操作建立连接、收发消息、关闭连接然后再执行第二个连接的所有操作。这种方法逻辑简单调试方便但两个连接在时间上是完全分离的无法模拟“同时在线”的并发场景只能算是“先后”连接对服务端的压力测试不够真实。方案二并行控制器模拟并发利用JMeter的Parallel Controller需要安装Custom Thread Groups插件或使用bzm - Parallel Controller。将两个WebSocket连接的建立步骤WebSocket Open Connection放在同一个Parallel Controller下这样JMeter会尝试同时发起这两个连接。建立连接后再用独立的逻辑处理各自的收发。这种方法能更真实地模拟两个连接同时建立的过程但对于连接建立后的长时间并行通信和复杂交互管理起来会变得比较繁琐控制器内部的取样器是并行执行一次不适合循环收发消息的场景。方案三多线程组协同单线程内复用这是我最推荐也是最终采用的方案。它巧妙地利用了JMeter的线程组和变量作用域。我们创建两个独立的线程组Thread Group但将它们的线程数都设置为1并且勾选Same user on each iteration每次迭代使用相同的用户。然后通过Test Plan级别的用户自定义变量User Defined Variables或使用__setProperty和__P函数在两个线程组之间传递连接名称等关键信息。这样从JMeter引擎的视角看这是两个“用户”但从业务逻辑上看因为线程数都是1且模拟的是同一个用户上下文它们共享测试计划状态可以实现两个连接在测试执行期间一直并存并独立进行各自的请求-响应循环。这个方案结构清晰两个连接完全独立、真正并行生命周期管理方便最贴近真实用户行为。我选择方案三因为它提供了最大的灵活性和真实性。接下来我们就以此为基础展开详细配置。3. 环境准备与插件安装工欲善其事必先利其器。在开始构建脚本之前确保你的测试环境已经就绪。3.1 安装JMeter与Java环境JMeter是纯Java应用所以首先需要安装JDK或JRE版本8及以上推荐JDK 11或17。去Oracle官网或Adoptium等开源站点下载安装并配置好JAVA_HOME环境变量。然后从Apache JMeter官网下载最新的二进制包例如apache-jmeter-5.6.3.zip解压到任意目录即可。无需安装进入bin目录双击jmeter.batWindows或运行jmeterLinux/macOS即可启动GUI界面。对于压测建议在无GUI模式下运行jmeter -n -t test.jmx -l result.jtl以减少资源消耗。3.2 安装WebSocket插件JMeter原生不支持WebSocket我们必须安装第三方插件。最常用的是WebSocket Samplers by Maciej Zaleski。访问JMeter插件管理网站plugins-manager下载jmeter-plugins-manager-*.jar文件。将这个jar包放入JMeter安装目录的lib/ext子目录下。重启JMeter你会在Options菜单中看到Plugins Manager选项。打开Plugins Manager切换到Available Plugins标签页。在搜索框中输入“WebSocket”找到WebSocket Samplers by Maciej Zaleski勾选它并点击Apply Changes and Restart JMeter进行安装。安装成功后你可以在取样器的添加菜单中看到新增的WebSocket分类里面包含我们需要的几个取样器。3.3 规划测试脚本结构在动手前先在纸上或脑图中规划好脚本结构这对管理双连接至关重要。我的规划如下测试计划Test Plan定义全局变量如服务器地址、端口、路径等。线程组AThread Group 1命名为“WS_Connection_A”。线程数1循环次数永远或指定次数负责管理第一个WebSocket连接例如“行情连接A”。线程组BThread Group 2命名为“WS_Connection_B”。线程数1循环次数永远负责管理第二个WebSocket连接例如“行情连接B”。两个线程组内部结构基本对称WebSocket Open Connection- 循环控制器包含WebSocket request-response Sampler和定时器 -WebSocket Close。监听器Listeners添加View Results Tree用于调试添加Summary Report或Aggregate Report用于查看压测结果。注意监听器最好放在测试计划层级以便收集两个线程组的所有数据。注意JMeter的线程组默认是并行执行的。这意味着一旦启动测试线程组A和线程组B会几乎同时开始运行从而实现两个连接的真正并行建立与通信。这正是我们想要的效果。4. 核心配置详解构建双WS连接脚本现在我们进入具体的配置环节。我将以模拟两个实时数据看板为例假设连接地址分别是ws://localhost:8080/ws/market/a和ws://localhost:8080/ws/market/b。4.1 定义全局变量首先在Test Plan层面设置用户自定义变量User Defined Variables这样所有线程组都能访问。我通常设置server_host: localhostserver_port: 8080ws_path_a: /ws/market/aws_path_b: /ws/market/bconnection_name_a: MarketConnection_Aconnection_name_b: MarketConnection_B使用变量的好处是显而易见的如果需要更换测试服务器或路径只需在一处修改维护效率大大提升。4.2 配置线程组A第一个WS连接添加一个Thread Group命名为“WS_Connection_A”。线程属性设置Number of Threads (users) 1Ramp-up period (seconds) 0Loop Count 勾选Forever或者根据测试需求设置具体循环次数。在线程组下添加WebSocket Open Connection取样器。Server Name or IP:${server_host}Port Number:${server_port}Path:${ws_path_a}Connection Name:${connection_name_a}这是关键为连接赋予唯一标识Implementation: 默认RFC6455即可。Response Timeout: 根据网络情况设置如5000毫秒。添加一个While Controller作为消息循环。条件设置为true或一个判断条件如${__javaScript(${C} 100)}表示循环100次。这个控制器用来模拟持续监听。在While Controller内添加WebSocket request-response Sampler。Connection Name:${connection_name_a}必须与Open Connection中的名称一致Request Data: 这里填写要发送给服务器的消息。可以是固定的如{type:subscribe,symbol:BTCUSD}也可以使用__Random、__time等函数动态生成。对于只接收服务器推送的场景这里可以留空但取样器仍会等待一个响应帧。Response Timeout: 设置等待服务器响应的超时时间。如果服务器是单向推送你可能需要将其设置得短一些或者使用WebSocket Single Read Sampler。Close Connection?: 不要勾选。在WebSocket request-response Sampler后可以添加一个Constant Timer设置延迟时间如1000毫秒用来控制接收消息的频率避免对服务器造成洪水攻击。最后在线程组末尾While Controller外添加一个WebSocket Close取样器Connection Name同样填写${connection_name_a}用于在测试结束时优雅地关闭连接。4.3 配置线程组B第二个WS连接重复步骤4.2创建第二个线程组“WS_Connection_B”。关键区别在于所有用到连接名称的地方改为${connection_name_b}。WebSocket Open Connection中的Path改为${ws_path_b}。两个连接发送的Request Data可以根据业务需求设置为不同的内容以模拟不同的数据订阅请求。4.4 添加监听器与调试在Test Plan下添加View Results Tree。在调试阶段它必不可少可以查看每个取样器的请求和响应详情确认连接是否成功建立、消息是否正常收发。对于压测添加Summary Report或Aggregate Report来查看聚合性能指标如吞吐量、响应时间、错误率等。为了区分两个连接的数据你可以在每个WebSocket request-response Sampler中设置不同的Sample Label例如“WS_A_Request”和“WS_B_Request”。至此一个基础的双WebSocket连接压测脚本就搭建完成了。运行后JMeter会启动两个线程分别建立和维护各自的WS连接并按照控制器逻辑进行通信。5. 高级技巧与参数化实战基础的脚本只能跑通流程要模拟真实复杂的场景还需要一些高级技巧。5.1 动态消息与参数化真实场景中客户端发送的消息不会是固定的。我们可以利用JMeter丰富的函数来动态生成数据。时间戳在请求数据中使用${__time()}或${__time(yyyy-MM-dd HH:mm:ss)}来添加时间戳。随机数使用${__Random(1,100,)}生成随机价格或数量。变量引用可以从CSV文件读取数据。添加一个CSV Data Set Config配置文件名和变量名。然后在请求数据中引用这些变量如{symbol:${symbol_from_csv},price:${__Random(100,200,)}}。这里有个关键点CSV Data Set Config的作用域。如果放在Test Plan下两个线程组会共享文件指针可能导致数据错乱。更稳妥的做法是为每个线程组单独配置一个CSV Data Set Config或者使用不同的变量名。5.2 处理服务器主动推送很多WebSocket服务是服务器主动推送数据客户端只是监听。对于这种场景WebSocket request-response Sampler可能不太合适因为它默认是发送一个请求然后等待一个响应。更优的选择是使用WebSocket Single Read Sampler。这个取样器只执行“读”操作会等待直到从指定连接收到一个消息帧或者超时。你可以把它放在一个循环控制器里用来持续监听服务器推送。记得配置合理的Read Timeout避免长时间阻塞。5.3 连接稳定性与重连机制网络是不稳定的WebSocket连接可能会意外断开。我们需要增强脚本的健壮性。添加响应断言在WebSocket Open Connection后添加一个Response Assertion检查返回的HTTP状态码是否是101Switching Protocols确保连接升级成功。利用后置处理器可以添加JSR223 PostProcessor在WebSocket request-response Sampler之后运行一段Groovy脚本检查响应内容或连接状态。如果发现连接异常例如响应为空或包含错误信息可以尝试使用vars.put(“reconnect_needed”, “true”)设置一个标志。实现条件重连在While Controller内部WebSocket request-response Sampler之前添加一个If Controller。条件判断是否为${reconnect_needed}。如果为真则在If控制器内部先执行一个WebSocket Close再执行一个新的WebSocket Open Connection最后将reconnect_needed变量置为false。这样就实现了一个简单的重连逻辑。5.4 控制两个连接间的交互有时我们需要模拟两个连接间的行为关联。例如连接A收到某个特定消息后连接B才发送请求。这可以通过JMeter的属性Properties功能来实现因为属性是全局的。在连接A的某个取样器后添加一个JSR223 PostProcessor使用props.put(“trigger_event”, “fired”)设置一个全局属性。在连接B的线程组中在需要等待的控制器如一个While Controller条件里使用${__P(trigger_event)}来读取这个属性。可以写成${__javaScript(“${__P(trigger_event,)}” “fired”)}。这样连接B就会等待直到连接A触发该事件。6. 调试与常见问题排坑实录在实际操作中你几乎一定会遇到各种问题。下面是我踩过的一些坑和解决方案。6.1 连接建立失败Gateway: not reachable url: ws://...这是最常见的问题。首先请逐项检查服务器是否真的在运行用浏览器WebSocket测试工具如浏览器插件“Simple WebSocket Client”或wscat命令行工具先验证服务端是否正常。地址、端口、路径是否正确特别注意WS路径是否以/开头是否与服务端定义的路径完全匹配。防火墙或网络策略确保JMeter所在机器可以访问目标服务器的指定端口。如果是本地回环地址127.0.0.1通常没问题如果是远程服务器需要检查网络连通性。JMeter代理设置如果你为JMeter设置了HTTP代理它可能会干扰WebSocket连接。在HTTP Request Defaults中检查或暂时关闭代理试试。6.2 连接被意外关闭或响应超时检查Response Timeout设置在WebSocket Open Connection和WebSocket request-response Sampler中都有这个参数。如果设置得太短在网络延迟高或服务器处理慢时连接可能在完全建立或收到响应前就被判定为超时。适当调大这个值比如从5000调到30000毫秒。服务器端空闲超时很多WebSocket服务器如Spring WebSocket有连接空闲超时设置。如果客户端长时间不发送任何数据Ping帧或业务消息服务器会主动关闭连接。解决方案是在JMeter脚本中定期发送Ping帧。可以添加一个WebSocket Ping/Pong Sampler放在循环控制器里设置一个较长的发送间隔如30秒。消息格式不符服务器可能期望特定格式的消息如JSON、特定协议头。使用View Results Tree查看你发送的Request Data的原始内容确保其符合服务端要求。有时需要添加Content-Type头但WebSocket是帧传输通常消息体本身格式正确即可。6.3 两个连接的数据互相干扰或错乱根本原因变量作用域污染。这是实现双连接时最容易出错的地方。如果你在Test Plan下定义了一个变量message并在两个线程组里都使用${message}那么一个线程组修改了这个变量另一个线程组读取到的值也会改变。解决方案为每个连接使用完全独立的变量名如message_a,message_b。善用线程局部变量在线程组内使用User Parameters预处理器或JSR223元件创建的变量默认是线程局部的不会影响其他线程组。谨慎使用__setProperty函数这个函数设置的是全局属性JVM属性所有线程共享。除非你明确需要全局通信否则避免用它来传递频繁变化的数据。6.4 性能测试时内存溢出OOM当模拟大量虚拟用户每个用户双连接时JMeter可能很快耗尽内存。调整JVM堆内存编辑JMeter的启动脚本jmeter.batWindows或jmeterUnix找到HEAP设置例如将-Xms1g -Xmx1g修改为-Xms4g -Xmx4g根据你的机器内存调整不要超过物理内存的70%。减少监听器开销View Results Tree会保存所有请求/响应的详细信息非常消耗内存。在正式压测时务必禁用或删除它只使用Summary Report、Aggregate Graph这类聚合监听器并将结果写入文件.jtl。优化脚本逻辑避免在循环中创建不必要的对象或进行复杂的字符串拼接尤其在JSR223元件中。保持取样器逻辑简洁。6.5 WebSocket插件版本兼容性问题不同版本的JMeter和WebSocket插件可能存在兼容性问题。如果你遇到一些诡异的行为如连接无法关闭、内存泄漏尝试升级到JMeter和插件的最新稳定版。查看插件的官方issue页面看是否有已知问题。作为一个临时排查手段可以尝试回退到之前稳定工作的版本组合。调试双WS连接脚本我的心得是“分而治之逐步集成”。先确保单个WS连接脚本能完美运行包括连接、通信、关闭。然后再复制一份修改连接名称和路径形成第二个独立脚本。最后将两个脚本合并到同一个测试计划中并仔细检查所有变量名和连接名称确保没有冲突。在View Results Tree中通过筛选不同的Sample Label可以清晰地看到两个连接各自的请求流这是定位问题最有效的方法。
JMeter实现单用户双WebSocket连接压测:方案详解与实战
1. 项目概述为什么要在JMeter里搞双WebSocket连接最近在做一个实时数据大屏的性能压测后端用的是WebSocket协议推数据。一开始我用JMeter的WebSocket Sampler模拟单个用户连接测起来挺顺利。但业务方提了个新需求他们想模拟一个用户同时打开两个不同的数据看板每个看板都通过独立的WebSocket连接接收不同的数据流。换句话说就是要在一个JMeter线程模拟一个用户里同时维持两个活跃的WebSocket连接并且能独立收发消息。这个需求一下就让我有点犯难了因为JMeter的官方WebSocket插件默认设计是“一个取样器管理一个连接”想在一个线程里玩转双WS得动点脑筋。这其实就是典型的“单用户多会话”场景。比如一个交易员同时盯着外汇和股票两个行情窗口或者一个运维人员同时监控服务器CPU和网络流量两个实时图表。如果只用单连接要么得合并数据流增加服务端解析复杂度要么就得开两个JMeter线程但这模拟的是两个用户不符合“一个用户两个标签页”的真实场景。所以实现双WebSocket连接核心目标是在一个虚拟用户VU线程内创建并管理两个完全独立的WebSocket会话让它们能并行工作互不干扰。这对于评估服务端在高并发真实用户场景下的连接管理、消息路由和资源消耗能力至关重要。2. 核心思路与方案选型JMeter实现双WS的几种玩法要实现这个目标首先得拆解JMeter处理WebSocket的机制。目前社区主流用的是WebSocket Samplers插件作者是Maciej Zaleski它提供了WebSocket Open Connection,WebSocket request-response Sampler,WebSocket Ping/Pong,WebSocket Close这几个取样器。每个WebSocket Open Connection取样器在执行时会建立一个WebSocket连接并将这个连接对象Session存储在一个以Connection Name为键的全局映射里。后续的WebSocket request-response Sampler通过指定相同的Connection Name来复用这个连接进行通信。基于这个原理实现双WS连接就有了清晰的路径创建两个具有不同Connection Name的连接并在同一个线程组内妥善管理它们的生命周期。主要有三种实现思路方案一顺序串行独立管理这是最直观的方法。在线程组里先完整执行第一个WS连接的所有操作建立连接、收发消息、关闭连接然后再执行第二个连接的所有操作。这种方法逻辑简单调试方便但两个连接在时间上是完全分离的无法模拟“同时在线”的并发场景只能算是“先后”连接对服务端的压力测试不够真实。方案二并行控制器模拟并发利用JMeter的Parallel Controller需要安装Custom Thread Groups插件或使用bzm - Parallel Controller。将两个WebSocket连接的建立步骤WebSocket Open Connection放在同一个Parallel Controller下这样JMeter会尝试同时发起这两个连接。建立连接后再用独立的逻辑处理各自的收发。这种方法能更真实地模拟两个连接同时建立的过程但对于连接建立后的长时间并行通信和复杂交互管理起来会变得比较繁琐控制器内部的取样器是并行执行一次不适合循环收发消息的场景。方案三多线程组协同单线程内复用这是我最推荐也是最终采用的方案。它巧妙地利用了JMeter的线程组和变量作用域。我们创建两个独立的线程组Thread Group但将它们的线程数都设置为1并且勾选Same user on each iteration每次迭代使用相同的用户。然后通过Test Plan级别的用户自定义变量User Defined Variables或使用__setProperty和__P函数在两个线程组之间传递连接名称等关键信息。这样从JMeter引擎的视角看这是两个“用户”但从业务逻辑上看因为线程数都是1且模拟的是同一个用户上下文它们共享测试计划状态可以实现两个连接在测试执行期间一直并存并独立进行各自的请求-响应循环。这个方案结构清晰两个连接完全独立、真正并行生命周期管理方便最贴近真实用户行为。我选择方案三因为它提供了最大的灵活性和真实性。接下来我们就以此为基础展开详细配置。3. 环境准备与插件安装工欲善其事必先利其器。在开始构建脚本之前确保你的测试环境已经就绪。3.1 安装JMeter与Java环境JMeter是纯Java应用所以首先需要安装JDK或JRE版本8及以上推荐JDK 11或17。去Oracle官网或Adoptium等开源站点下载安装并配置好JAVA_HOME环境变量。然后从Apache JMeter官网下载最新的二进制包例如apache-jmeter-5.6.3.zip解压到任意目录即可。无需安装进入bin目录双击jmeter.batWindows或运行jmeterLinux/macOS即可启动GUI界面。对于压测建议在无GUI模式下运行jmeter -n -t test.jmx -l result.jtl以减少资源消耗。3.2 安装WebSocket插件JMeter原生不支持WebSocket我们必须安装第三方插件。最常用的是WebSocket Samplers by Maciej Zaleski。访问JMeter插件管理网站plugins-manager下载jmeter-plugins-manager-*.jar文件。将这个jar包放入JMeter安装目录的lib/ext子目录下。重启JMeter你会在Options菜单中看到Plugins Manager选项。打开Plugins Manager切换到Available Plugins标签页。在搜索框中输入“WebSocket”找到WebSocket Samplers by Maciej Zaleski勾选它并点击Apply Changes and Restart JMeter进行安装。安装成功后你可以在取样器的添加菜单中看到新增的WebSocket分类里面包含我们需要的几个取样器。3.3 规划测试脚本结构在动手前先在纸上或脑图中规划好脚本结构这对管理双连接至关重要。我的规划如下测试计划Test Plan定义全局变量如服务器地址、端口、路径等。线程组AThread Group 1命名为“WS_Connection_A”。线程数1循环次数永远或指定次数负责管理第一个WebSocket连接例如“行情连接A”。线程组BThread Group 2命名为“WS_Connection_B”。线程数1循环次数永远负责管理第二个WebSocket连接例如“行情连接B”。两个线程组内部结构基本对称WebSocket Open Connection- 循环控制器包含WebSocket request-response Sampler和定时器 -WebSocket Close。监听器Listeners添加View Results Tree用于调试添加Summary Report或Aggregate Report用于查看压测结果。注意监听器最好放在测试计划层级以便收集两个线程组的所有数据。注意JMeter的线程组默认是并行执行的。这意味着一旦启动测试线程组A和线程组B会几乎同时开始运行从而实现两个连接的真正并行建立与通信。这正是我们想要的效果。4. 核心配置详解构建双WS连接脚本现在我们进入具体的配置环节。我将以模拟两个实时数据看板为例假设连接地址分别是ws://localhost:8080/ws/market/a和ws://localhost:8080/ws/market/b。4.1 定义全局变量首先在Test Plan层面设置用户自定义变量User Defined Variables这样所有线程组都能访问。我通常设置server_host: localhostserver_port: 8080ws_path_a: /ws/market/aws_path_b: /ws/market/bconnection_name_a: MarketConnection_Aconnection_name_b: MarketConnection_B使用变量的好处是显而易见的如果需要更换测试服务器或路径只需在一处修改维护效率大大提升。4.2 配置线程组A第一个WS连接添加一个Thread Group命名为“WS_Connection_A”。线程属性设置Number of Threads (users) 1Ramp-up period (seconds) 0Loop Count 勾选Forever或者根据测试需求设置具体循环次数。在线程组下添加WebSocket Open Connection取样器。Server Name or IP:${server_host}Port Number:${server_port}Path:${ws_path_a}Connection Name:${connection_name_a}这是关键为连接赋予唯一标识Implementation: 默认RFC6455即可。Response Timeout: 根据网络情况设置如5000毫秒。添加一个While Controller作为消息循环。条件设置为true或一个判断条件如${__javaScript(${C} 100)}表示循环100次。这个控制器用来模拟持续监听。在While Controller内添加WebSocket request-response Sampler。Connection Name:${connection_name_a}必须与Open Connection中的名称一致Request Data: 这里填写要发送给服务器的消息。可以是固定的如{type:subscribe,symbol:BTCUSD}也可以使用__Random、__time等函数动态生成。对于只接收服务器推送的场景这里可以留空但取样器仍会等待一个响应帧。Response Timeout: 设置等待服务器响应的超时时间。如果服务器是单向推送你可能需要将其设置得短一些或者使用WebSocket Single Read Sampler。Close Connection?: 不要勾选。在WebSocket request-response Sampler后可以添加一个Constant Timer设置延迟时间如1000毫秒用来控制接收消息的频率避免对服务器造成洪水攻击。最后在线程组末尾While Controller外添加一个WebSocket Close取样器Connection Name同样填写${connection_name_a}用于在测试结束时优雅地关闭连接。4.3 配置线程组B第二个WS连接重复步骤4.2创建第二个线程组“WS_Connection_B”。关键区别在于所有用到连接名称的地方改为${connection_name_b}。WebSocket Open Connection中的Path改为${ws_path_b}。两个连接发送的Request Data可以根据业务需求设置为不同的内容以模拟不同的数据订阅请求。4.4 添加监听器与调试在Test Plan下添加View Results Tree。在调试阶段它必不可少可以查看每个取样器的请求和响应详情确认连接是否成功建立、消息是否正常收发。对于压测添加Summary Report或Aggregate Report来查看聚合性能指标如吞吐量、响应时间、错误率等。为了区分两个连接的数据你可以在每个WebSocket request-response Sampler中设置不同的Sample Label例如“WS_A_Request”和“WS_B_Request”。至此一个基础的双WebSocket连接压测脚本就搭建完成了。运行后JMeter会启动两个线程分别建立和维护各自的WS连接并按照控制器逻辑进行通信。5. 高级技巧与参数化实战基础的脚本只能跑通流程要模拟真实复杂的场景还需要一些高级技巧。5.1 动态消息与参数化真实场景中客户端发送的消息不会是固定的。我们可以利用JMeter丰富的函数来动态生成数据。时间戳在请求数据中使用${__time()}或${__time(yyyy-MM-dd HH:mm:ss)}来添加时间戳。随机数使用${__Random(1,100,)}生成随机价格或数量。变量引用可以从CSV文件读取数据。添加一个CSV Data Set Config配置文件名和变量名。然后在请求数据中引用这些变量如{symbol:${symbol_from_csv},price:${__Random(100,200,)}}。这里有个关键点CSV Data Set Config的作用域。如果放在Test Plan下两个线程组会共享文件指针可能导致数据错乱。更稳妥的做法是为每个线程组单独配置一个CSV Data Set Config或者使用不同的变量名。5.2 处理服务器主动推送很多WebSocket服务是服务器主动推送数据客户端只是监听。对于这种场景WebSocket request-response Sampler可能不太合适因为它默认是发送一个请求然后等待一个响应。更优的选择是使用WebSocket Single Read Sampler。这个取样器只执行“读”操作会等待直到从指定连接收到一个消息帧或者超时。你可以把它放在一个循环控制器里用来持续监听服务器推送。记得配置合理的Read Timeout避免长时间阻塞。5.3 连接稳定性与重连机制网络是不稳定的WebSocket连接可能会意外断开。我们需要增强脚本的健壮性。添加响应断言在WebSocket Open Connection后添加一个Response Assertion检查返回的HTTP状态码是否是101Switching Protocols确保连接升级成功。利用后置处理器可以添加JSR223 PostProcessor在WebSocket request-response Sampler之后运行一段Groovy脚本检查响应内容或连接状态。如果发现连接异常例如响应为空或包含错误信息可以尝试使用vars.put(“reconnect_needed”, “true”)设置一个标志。实现条件重连在While Controller内部WebSocket request-response Sampler之前添加一个If Controller。条件判断是否为${reconnect_needed}。如果为真则在If控制器内部先执行一个WebSocket Close再执行一个新的WebSocket Open Connection最后将reconnect_needed变量置为false。这样就实现了一个简单的重连逻辑。5.4 控制两个连接间的交互有时我们需要模拟两个连接间的行为关联。例如连接A收到某个特定消息后连接B才发送请求。这可以通过JMeter的属性Properties功能来实现因为属性是全局的。在连接A的某个取样器后添加一个JSR223 PostProcessor使用props.put(“trigger_event”, “fired”)设置一个全局属性。在连接B的线程组中在需要等待的控制器如一个While Controller条件里使用${__P(trigger_event)}来读取这个属性。可以写成${__javaScript(“${__P(trigger_event,)}” “fired”)}。这样连接B就会等待直到连接A触发该事件。6. 调试与常见问题排坑实录在实际操作中你几乎一定会遇到各种问题。下面是我踩过的一些坑和解决方案。6.1 连接建立失败Gateway: not reachable url: ws://...这是最常见的问题。首先请逐项检查服务器是否真的在运行用浏览器WebSocket测试工具如浏览器插件“Simple WebSocket Client”或wscat命令行工具先验证服务端是否正常。地址、端口、路径是否正确特别注意WS路径是否以/开头是否与服务端定义的路径完全匹配。防火墙或网络策略确保JMeter所在机器可以访问目标服务器的指定端口。如果是本地回环地址127.0.0.1通常没问题如果是远程服务器需要检查网络连通性。JMeter代理设置如果你为JMeter设置了HTTP代理它可能会干扰WebSocket连接。在HTTP Request Defaults中检查或暂时关闭代理试试。6.2 连接被意外关闭或响应超时检查Response Timeout设置在WebSocket Open Connection和WebSocket request-response Sampler中都有这个参数。如果设置得太短在网络延迟高或服务器处理慢时连接可能在完全建立或收到响应前就被判定为超时。适当调大这个值比如从5000调到30000毫秒。服务器端空闲超时很多WebSocket服务器如Spring WebSocket有连接空闲超时设置。如果客户端长时间不发送任何数据Ping帧或业务消息服务器会主动关闭连接。解决方案是在JMeter脚本中定期发送Ping帧。可以添加一个WebSocket Ping/Pong Sampler放在循环控制器里设置一个较长的发送间隔如30秒。消息格式不符服务器可能期望特定格式的消息如JSON、特定协议头。使用View Results Tree查看你发送的Request Data的原始内容确保其符合服务端要求。有时需要添加Content-Type头但WebSocket是帧传输通常消息体本身格式正确即可。6.3 两个连接的数据互相干扰或错乱根本原因变量作用域污染。这是实现双连接时最容易出错的地方。如果你在Test Plan下定义了一个变量message并在两个线程组里都使用${message}那么一个线程组修改了这个变量另一个线程组读取到的值也会改变。解决方案为每个连接使用完全独立的变量名如message_a,message_b。善用线程局部变量在线程组内使用User Parameters预处理器或JSR223元件创建的变量默认是线程局部的不会影响其他线程组。谨慎使用__setProperty函数这个函数设置的是全局属性JVM属性所有线程共享。除非你明确需要全局通信否则避免用它来传递频繁变化的数据。6.4 性能测试时内存溢出OOM当模拟大量虚拟用户每个用户双连接时JMeter可能很快耗尽内存。调整JVM堆内存编辑JMeter的启动脚本jmeter.batWindows或jmeterUnix找到HEAP设置例如将-Xms1g -Xmx1g修改为-Xms4g -Xmx4g根据你的机器内存调整不要超过物理内存的70%。减少监听器开销View Results Tree会保存所有请求/响应的详细信息非常消耗内存。在正式压测时务必禁用或删除它只使用Summary Report、Aggregate Graph这类聚合监听器并将结果写入文件.jtl。优化脚本逻辑避免在循环中创建不必要的对象或进行复杂的字符串拼接尤其在JSR223元件中。保持取样器逻辑简洁。6.5 WebSocket插件版本兼容性问题不同版本的JMeter和WebSocket插件可能存在兼容性问题。如果你遇到一些诡异的行为如连接无法关闭、内存泄漏尝试升级到JMeter和插件的最新稳定版。查看插件的官方issue页面看是否有已知问题。作为一个临时排查手段可以尝试回退到之前稳定工作的版本组合。调试双WS连接脚本我的心得是“分而治之逐步集成”。先确保单个WS连接脚本能完美运行包括连接、通信、关闭。然后再复制一份修改连接名称和路径形成第二个独立脚本。最后将两个脚本合并到同一个测试计划中并仔细检查所有变量名和连接名称确保没有冲突。在View Results Tree中通过筛选不同的Sample Label可以清晰地看到两个连接各自的请求流这是定位问题最有效的方法。