菜单
开源

WebSockets

概览

WebSocket 是一种在单个 TCP 连接上提供全双工通信通道的协议。它常用于单页应用 (SPA) 和移动应用,以添加基于服务器推送的功能,这通常可以改善基于轮询解决方案的性能。

使用 k6 进行 WebSockets 负载测试

注意

xk6-websockets 是一个实验性模块,其 API 比 k6/ws 更标准。它实现了 WebSockets API 最新标准。尽管实现尚未完成,但它使用全局事件循环而非本地事件循环。

目前,它作为实验性模块 k6/experimental/websockets 提供。未来它也很可能成为 k6 核心的一部分。

将基于 HTTP 的测试与基于 WebSocket 的测试进行比较,您会发现它们在结构和内部工作原理上都存在差异。主要区别在于,每个 VU 现在运行一个异步事件循环,而不是重复地连续循环主函数 (export default function() { ... }) 。

WebSocket 测试的基本结构如下所示

JavaScript
import ws from 'k6/ws';
import { check } from 'k6';

export default function () {
  const url = 'ws://echo.websocket.org';
  const params = { tags: { my_tag: 'hello' } };

  const res = ws.connect(url, params, function (socket) {
    socket.on('open', () => console.log('connected'));
    socket.on('message', (data) => console.log('Message received: ', data));
    socket.on('close', () => console.log('disconnected'));
  });

  check(res, { 'status is 101': (r) => r && r.status === 101 });
}

在此示例中,connect() 方法将一个“运行”函数作为其第三个参数。该函数应接受一个 Socket 对象作为其唯一参数。运行函数构成了异步事件循环的基础。

创建 WebSocket 连接后,运行函数将立即被调用,其中的所有代码都将执行(通常是设置事件处理程序的代码),然后阻塞,直到 WebSocket 连接关闭(由远程主机关闭或使用 socket.close() 关闭)。

错误处理

要捕获 WebSocket 连接生命周期中发生的错误,请为“error”事件附加处理程序

JavaScript
import ws from 'k6/ws';
import { check } from 'k6';

export default function () {
  const url = 'ws://echo.websocket.org';
  const params = { tags: { my_tag: 'hello' } };

  const res = ws.connect(url, params, function (socket) {
    socket.on('open', function open() {
      // ...
    });

    socket.on('error', function (e) {
      if (e.error() != 'websocket: close sent') {
        console.log('An unexpected error occured: ', e.error());
      }
    });
  });

  check(res, { 'status is 101': (r) => r && r.status === 101 });
}

定时器

要调度重复操作,请使用 socket.setInterval 指定在特定间隔调用的函数。

JavaScript
import ws from 'k6/ws';
import { check } from 'k6';

export default function () {
  const url = 'ws://echo.websocket.org';
  const params = { tags: { my_tag: 'hello' } };

  const res = ws.connect(url, params, function (socket) {
    socket.on('open', function open() {
      console.log('connected');

      socket.setInterval(function timeout() {
        socket.ping();
        console.log('Pinging every 1sec (setInterval test)');
      }, 1000);
    });

    socket.on('ping', () => console.log('PING!'));
    socket.on('pong', () => console.log('PONG!'));
    socket.on('close', () => console.log('disconnected'));
  });

  check(res, { 'status is 101': (r) => r && r.status === 101 });
}

超时

要为 WebSocket 连接添加超时,请将处理程序函数和超时值(以毫秒为单位)都传递给 socket.setTimeout 函数。

JavaScript
import ws from 'k6/ws';
import { check } from 'k6';

export default function () {
  const url = 'ws://echo.websocket.org';
  const params = { tags: { my_tag: 'hello' } };

  const res = ws.connect(url, params, function (socket) {
    socket.on('open', () => console.log('connected'));
    socket.on('close', () => console.log('disconnected'));

    socket.setTimeout(function () {
      console.log('2 seconds passed, closing the socket');
      socket.close();
    }, 2000);
  });

  check(res, { 'status is 101': (r) => r && r.status === 101 });
}

在前面的示例中,超时将在 2 秒后关闭 WebSocket 连接。

多个事件处理程序

您可以为同一事件附加多个处理程序函数

JavaScript
import ws from 'k6/ws';
import { check } from 'k6';

export default function () {
  const url = 'ws://echo.websocket.org';
  const params = { tags: { my_tag: 'hello' } };

  const response = ws.connect(url, params, function (socket) {
    socket.on('open', function open() {
      console.log('connected');
      socket.send(Date.now());

      socket.setInterval(function timeout() {
        socket.ping();
        console.log('Pinging every 1sec (setInterval test)');
      }, 1000);
    });

    socket.on('ping', () => console.log('PING!'));
    socket.on('pong', () => console.log('PONG!'));
    socket.on('pong', () => {
      // Multiple event handlers on the same event
      console.log('OTHER PONG!');
    });

    socket.on('close', () => console.log('disconnected'));

    socket.on('error', (e) => {
      if (e.error() != 'websocket: close sent') {
        console.log('An unexpected error occurred: ', e.error());
      }
    });

    socket.setTimeout(function () {
      console.log('2 seconds passed, closing the socket');
      socket.close();
    }, 2000);
  });

  check(response, { 'status is 101': (r) => r && r.status === 101 });
}