【JMeter系列-10】JMeter websocket接口测试
前言
在一个网站中,很多数据需要即时更新,比如期货交易类的用户资产。在以前,这种功能的实现一般使用http
轮询,即客户端用定时任务每隔一段时间向服务器发送查询请求来获取最新值。这种方式的弊端显而易见:
- 有可能造成数据更新不及时,如果前端轮询频率为5s,也许数据在这5s内已经更新多次了。
- 有可能对数据库造成额外压力,例如一个用户资产长时间不变化,但客户端还是要定时去查询,这种无意义查询占比相当高,对服务器造成不必要的压力。
- 要经过请求和响应两次交互,增加了耗时,而且http请求可能携带大量的header信息,增加网络带宽占用
HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议-WebSocket
,很好地解决了http轮询的弊端。
在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。
也就是说,http
轮询机制,主动权完全在客户端,而WebSocket机制中,主动权可以交给服务端,数据推送可以更精确,包括何时推送(定时推送还是更新即推送),推送什么数据。
准备工作
JMeter可以非常便利地进行WebSocket
接口测试,但需要引入下列依赖:
- jetty-http
- jetty-io
- jetty-util
- websocket-api
- websocket-client
- websweocket-common
相关依赖下载:
https://pan.baidu.com/s/1PTOyTBzmOwLPNhxB-TxR7g ,提取码:uq25
下面内容基于JMeter5.1.1
将相关jar包放入JMeter安装目录的/lib/etc中,重启JMeter。在取样器中,可以看到比之前多了websweocket
相关的取样器。
脚本编写
在编写脚本之前,先要搞清楚推送服务的逻辑,它的逻辑是这样的:
- 首先,客户端向服务端发送请求,建立连接
- 建立连接后,客户端需要定时向服务端发送
ping-pong
消息,维持心跳 - 客户端发送主动断开连接的请求,服务端断开该连接
建立连接,使用【WebSocket request-response Sampler】,顾名思义,这个取样器既能发送请求也能接收响应。
说明一下各项内容:
-
Connection
:有use existing connection
和setup new connection
两种模式,前者是使用已有连接,即上一个websocket请求所建立的连接通道,选择后Server URL
全置灰只读不可操作。后者指新建连接通道。 -
Server URL
:ws协议和wss(加密的websocket)可选,sever name or IP
(服务器地址)、Port
(端口号)、Path
(路径)、Connection timeout
(连接超时时间)这些含义也很明了。 -
Data
:发送数据,可以选择Text(文本,包括JSON)和Binary(二进制)形式,也可以通过勾选Read request data from file
来从文件中获取data。
这个请求要与连接请求是同一个线程,并且要定时运行,因此设计脚本结构如下:
注意两点:
- 想要建立3000个连接的话,一定是将【线程数】设置为3000,循环次数设置为1,而不是相反,这是许多人容易弄混淆的。
- 【Ramp-up 时间】这个参数是全部线程启动的时间,如果想给服务器较大的瞬时压力,就把时间设置短一些。经过实测,这个时间太短的话,最终成功建立的连接会明显少于设置的【线程数】,所以一般设置长一点。
一般的websocket推送服务,会设计定时心跳检测机制,也就是客户端定时向服务端发送一条特定的消息,这样服务端就会保持这个连接,否则的话,这个客户端就被服务端判定为不活跃而被断掉连接。因此,为了让我们的脚本持续跑下去,就需要加入心跳检测请求。
因为线程循环次数是1次(多次的话,就是一个线程反复建立连接了),因此我们要把【心跳检测】放到一个循环控制器中。
而【固定定时器】的作用,就是控制【心跳检测】发送的频率:
心跳检测:
尤其要注意两点:
-
Connection
项一定要选择use existing connection
,表示使用该线程已经建立的连接,否则就重新创建连接了。 -
Request data
根据服务端的实现而定。
- Spring+SpringMVC+MyBatis+easyUI整合优化篇(十三)数据层优化-表规范、索引优化
- node中的Stream-Readable和Writeable解读
- Spring+SpringMVC+MyBatis+easyUI整合进阶篇(六)一定要RESTful吗?
- 深入node之Transform
- 巧用shell脚本统计磁盘使用情况(r4笔记第12天)
- 使用fasttext实现文本处理及文本预测
- 关于导入导出sequence(r4笔记第11天)
- Spring+SpringMVC+MyBatis整合进阶篇(四)RESTful实战(前端代码修改)
- Nodejs cluster模块深入探究
- org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression
- 巧用分析函数循序渐进解决实际问题 (r4笔记第10天)
- 支持多用户web终端实现及安全保障(nodejs)
- 你看到的最直白清晰的CNN讲解
- oracle中的数组(第一篇)(r4笔记第9天)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Python中字典的详细用法
- Android抓包总结-HTTPS单向认证&双向认证突破
- 2020 ISG“观安杯”最高分值web题的解题思路大放送
- 详解 JS 压缩图片
- LeetCode 1553. Minimum Number of Days to Eat N Oranges
- 异步IO数据库队列缓存
- markdown转为pdf文件
- [已解决]报错:Required request body is missing
- jupyter notebook修改默认路径和浏览器
- python selenium while 循环
- implicitly_wait()隐式等待
- [已解决]python FileNotFoundError: [WinError 3] for getsize(filepath)
- [已解决]ValueError: row index was 65536, not allowed by .xls format
- 记一次由Redis分布式锁造成的重大事故,避免以后踩坑!
- ES6部分源码重写 -1(ES5-构造函数解析)