Туториалы30 января 2025 г. · 9 мин чтения

Нагрузочное тестирование WebSocket API с k6 и Artillery

HTTP — не единственный протокол. Учимся симулировать тысячи одновременных WebSocket-соединений и измерять пропускную способность сообщений.

Автор: Команда Perfscale

Нагрузочное тестирование WebSocket отличается от HTTP-тестирования одним фундаментальным образом: вы измеряете постоянные соединения и пропускную способность сообщений, а не латентность запрос/ответ.

Пример WebSocket в k6

У k6 нативная поддержка WebSocket через модуль k6/ws:

import ws from 'k6/ws';
import { check } from 'k6';
import { Counter, Trend } from 'k6/metrics';

const msgSent = new Counter('messages_sent');
const msgReceived = new Counter('messages_received');
const msgLatency = new Trend('message_latency_ms');

export const options = {
  vus: 100,
  duration: '2m',
  thresholds: {
    messages_sent: ['count > 1000'],
    message_latency_ms: ['p(95)<200'],
  },
};

export default function () {
  const url = 'wss://ws.example.com/chat';
  const sendTime = {};

  const res = ws.connect(url, {}, function (socket) {
    socket.on('open', () => {
      // Send a ping every second
      const intervalId = socket.setInterval(() => {
        const id = Date.now().toString();
        sendTime[id] = Date.now();
        socket.send(JSON.stringify({ type: 'ping', id }));
        msgSent.add(1);
      }, 1000);

      socket.setTimeout(() => {
        socket.clearInterval(intervalId);
        socket.close();
      }, 30000);
    });

    socket.on('message', (data) => {
      const msg = JSON.parse(data);
      if (msg.type === 'pong' && sendTime[msg.id]) {
        msgLatency.add(Date.now() - sendTime[msg.id]);
        delete sendTime[msg.id];
        msgReceived.add(1);
      }
    });
  });

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

Что измерять

Для WebSocket-тестов стандартные HTTP-метрики не подходят. Смотрите на:

  • Скорость установления соединений — сколько соединений в секунду принимает сервер?
  • Пропускную способность сообщений — сообщений/с на соединение и суммарно
  • Латентность сообщений — время от отправки до получения подтверждения
  • Стабильность соединений — сколько соединений обрывается неожиданно?

Масштабирование WebSocket-тестов

Каждый VU в k6 держит открытое соединение. На 1 000 VU нужно 1 000 открытых соединений на машине-генераторе — проверьте ulimit -n перед большими тестами.

Пользователям Perfscale распределённое выполнение автоматически размазывает соединения по нескольким машинам, так что лимит файловых дескрипторов одного хоста перестаёт быть потолком.