websockets
注意
这是一个实验性模块。
虽然我们旨在尽可能保持实验性模块的稳定性,但我们可能需要引入破坏性更改。这可能会在未来的 k6 版本中发生,直到该模块完全稳定并毕业成为 k6 核心模块。欲了解更多信息,请参阅扩展毕业流程。
实验性模块保持高度稳定性,并遵循常规维护和安全措施。如果您有任何反馈或建议,欢迎提交 issue。
这个实验性 API 实现了浏览器 WebSocket API 并提供了额外的 k6 特定功能(如 cookie、标签、header 等)。
此模块与 k6/ws
的主要区别在于,此模块使用全局事件循环而非本地事件循环。全局事件循环允许单个虚拟用户建立多个并发连接,从而提高了性能。
该 WebSocket API
尚未完全实现,我们正在努力完善,但我们相信它对大多数用户来说是可用的。因此,无论您是正在编写新的 WebSocket 测试,还是当前正在使用 k6/ws
模块,我们诚邀您试用并报告项目中的issue 跟踪器。我们的中期目标是使该模块成为 k6 核心的一部分,长期目标是取代 k6/ws
模块。
类/方法 | 描述 |
---|---|
参数 | 用于设置各种 WebSocket 连接参数,例如 header、cookie jar、压缩等。 |
WebSocket(url, protocols, params) | 构造一个新的 WebSocket 连接。 |
WebSocket.close() | 关闭 WebSocket 连接。 |
WebSocket.ping() | 发送 ping。 |
WebSocket.send(data) | 发送数据。 |
WebSocket.addEventListener(event, handler) | 为特定事件在连接上添加事件监听器。 |
Blob | 表示 blob 的接口,blob 是一个类似文件的不可变原始数据对象;可以作为文本或二进制数据读取,或转换为 ReadableStream。 |
WebSocket 实例还具有以下属性
类/属性 | 描述 |
---|---|
WebSocket.readyState | 连接的当前状态。可以是四种状态之一。 |
WebSocket.url | 由构造函数解析的连接 URL。 |
WebSocket.bufferedAmount | 已使用调用 send() 排队但尚未传输到网络的数据字节数。 |
WebSocket.binaryType | 该binaryType 控制通过 WebSocket 连接接收的二进制数据类型。使用 "blob" 以将其作为 Blob 接收,或使用 "arraybuffer" 以将其作为 ArrayBuffer 接收。默认值为 "blob" 。 |
WebSocket.onmessage | 用于处理 message 事件的处理器。 |
WebSocket.onerror | 用于处理 error 事件的处理器。 |
WebSocket.onopen | 用于处理 open 事件的处理器。 |
WebSocket.onclose | 用于处理 close 事件的处理器。 |
WebSocket.onping | 用于处理 ping 事件的处理器。 |
WebSocket.onpong | 用于处理 pong 事件的处理器。 |
Websocket 指标
k6 会针对 Websocket 进行特定的测量。有关完整列表,请参阅指标参考。
示例
此示例展示了
- 单个虚拟用户如何异步运行多个 WebSocket 连接。
- 如何使用 timeout 和 interval 函数在一段时间后停止连接。
import { randomString, randomIntBetween } from 'https://jslib.k6.io/k6-utils/1.1.0/index.js';
import { WebSocket } from 'k6/experimental/websockets';
const sessionDuration = randomIntBetween(1000, 3000); // user session between 1s and 3s
export default function () {
for (let i = 0; i < 4; i++) {
startWSWorker(i);
}
}
function startWSWorker(id) {
// create a new websocket connection
const ws = new WebSocket(`wss://quickpizza.grafana.com/ws`);
ws.binaryType = 'arraybuffer';
ws.addEventListener('open', () => {
// change the user name
ws.send(JSON.stringify({ event: 'SET_NAME', new_name: `VU ${__VU}:${id}` }));
// listen for messages/errors and log them into console
ws.addEventListener('message', (e) => {
const msg = JSON.parse(e.data);
if (msg.event === 'CHAT_MSG') {
console.log(`VU ${__VU}:${id} received: ${msg.user} says: ${msg.message}`);
} else if (msg.event === 'ERROR') {
console.error(`VU ${__VU}:${id} received:: ${msg.message}`);
} else {
console.log(`VU ${__VU}:${id} received unhandled message: ${msg.message}`);
}
});
// send a message every 2-8 seconds
const intervalId = setInterval(() => {
ws.send(JSON.stringify({ event: 'SAY', message: `I'm saying ${randomString(5)}` }));
}, randomIntBetween(2000, 8000)); // say something every 2-8 seconds
// after a sessionDuration stop sending messages and leave the room
const timeout1id = setTimeout(function () {
clearInterval(intervalId);
console.log(`VU ${__VU}:${id}: ${sessionDuration}ms passed, leaving the chat`);
ws.send(JSON.stringify({ event: 'LEAVE' }));
}, sessionDuration);
// after a sessionDuration + 3s close the connection
const timeout2id = setTimeout(function () {
console.log(`Closing the socket forcefully 3s after graceful LEAVE`);
ws.close();
}, sessionDuration + 3000);
// when connection is closing, clean up the previously created timers
ws.addEventListener('close', () => {
clearTimeout(timeout1id);
clearTimeout(timeout2id);
console.log(`VU ${__VU}:${id}: disconnected`);
});
});
}