菜单
开源 RSS

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.binaryTypebinaryType 控制通过 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 函数在一段时间后停止连接。
JavaScript
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`);
    });
  });
}