Нагружаем веб-сокеты без боли

Обо мне

  • SDET с 2018
  • Senior SDET @b2broker
  • Certified node.js application developer (JSNAD 2023)
  • автор TG канала @haradkou_sdet
  • Консультирую кампании
  • Ментор

Telegram: haradkou_sdet

План

  • контекст проекта

  • наша цель

  • подготовка к нагрузке

  • сценарий

  • итоги и выводы

Контекст проекта

  • Trading terminal
  • Работа с финансовыми инструментами

Трейдинг терминал

Фото из официального сайта b2broker.com

Фото взято с официального сайта b2broker.com/b2trader

Контекст Запуска

  • .NET signalR in server + транспорты
    • WebSocket
    • SSE
    • Long Polling
    • Forever Frame (IE)
  • 1 инстанс каждого сервиса без автоскейлинга
  • Автоскейлинг WebSocket довольно сложная задача

WebSocket

SignalR

(картинка с negotiate)

Negotiate -> Transports + access token -> WSS:// 

Сценарии

  • Получения цен в реальном времени - market data
  • Создание CFD сделки

Цель нагрузки

Узнать о просадке системы в оптимальном режиме (5 открытых позиции для пользователя)

Боль 1

Sticky sessions & масштабирование

Sticky Sessions

"Прилипшие сессии" - запросы от одного и того же клиента всегда попадали на один и тот же сервер

Sticky Sessions

Решение. Sticky Sessions

Не тестируем (пока что)

Решение. Sticky Sessions

Решение. Sticky Sessions

Боль 2

Business. CFD

Что такое CFD сделка?

CFD - Contract For Difference («контракт на разницу») - это финансовый дериватив. При его открытии ты не покупаешь актив, а заключаешь договор на изменение его цены. 

Путь сделки

 

  1. Указывается актив — акция, индекс, криптовалюта и т.д.
  2. Указывается направление — покупка или продажа.

  3. Указывается объём (лот) — сколько единиц актива.

  4. Брокер фиксирует цену открытия — например, цена биткоина $60,000.

  5. Создаётся запись у брокера — в их базе данных или системе, что ты открыл позицию.

📉 При закрытии сделки считается разница между ценой открытия и закрытия, и эта разница — твоя прибыль или убыток.

Причем здесь WebSocket

После создания позиции, ее (позицию) нужно "слушать" на изменение цены. Если слишком большая "просадка" цены, то вызывается margin call.

Ограничения

  1. Реальный трейдер не работает одновременно больше чем 3-5 открытых позициях
  2. Нагрузочные тесты работают с VUS (витруальными пользователями)
  3. Нагрузочные тесты только создают CFD сделку, но не закрывают позицию
  4. 1 VUS может создать несколько CFD сделок за несколько итераций

Боль 3

K6

  • 30-40k VUS - Limit per k6 instance
  • Лимит памяти 1 контейнера внутри паиплаина - DND. Infrastructure
  • Сделки нужно держать в пределах 3-5 для пользователя

K6 Ограничение

  • 30-40k VUS - Limit per k6 instance
  • Лимит памяти 1 контейнера внутри паиплаина - DND. Infrastructure
  • Сделки нужно держать в пределах 3-5 для пользователя

Сам сценарий

export const options: Options = {
  vus: VUS,
  iterations: ITERATIONS
  duration: DURATION,
  tags: {
    PROMETHEUS_ID: K6_PROMETHEUS_ID,
  },
}

export default function (){
  for (let i = 0; i < CFD_ORDERS_COUNT; i++) {
      apiV4CfdOrders.order({
        marketId: K6_CFD_MARKET_ID,
        orderType: 'Market',
      })
    }
}

Helper. JS

function processRowInfinite(row){
  const socket = new WebSocket(`${serverUrl}/?token=${row.token}`)
  socket.send('GetOpenPositions', row.accountId)
  
  socket.onMessage = (msg) => {
    const deserialized = JSON.parse(msg)
    if(deserialized.length > K6_HELPER_OPEN_POSITION_COUNT) {
      const firstPos = deserialized.items[0]
      fetch(`${serverUrl}/positions/${firstPos.id}/close`, {
        method: 'POST',
      })
    }
  }
}

while(true){
  csvParsed.map(row => {
	processRowInfinite(row)      
  })
}

Боль 4

Инфраструкрура

  • DND (Docker in Docker)
  • Victoria Metrics хранит срезы до 2-3 недели
  • Грамотно упаковать паиплаин
  • Поднятие чистого окружения, записи в DNS, очистка окружения

Пайплайн

Init env -> k6 tests -> metrics -> destroy env

Решение

  • Отчеты
    • Отчеты со срезами основных метрик в confluence
    • Отчеты в слак для автоматики
  • Увеличенные лимиты для паиплаина и стенда в целом
  • Разделение задач для K6 и helper​
  • Окружение
    • Уничтожение старого окружения при запуске нового
    • Отдельное окружение для запуска по расписанию
    • Удлинение время жизни токенов пользователей

Пайплайн

Отчеты

Start Time Duration RT CFD RPS CFD ...
2025-05-12 600 12 500 ...
2025-05-15 600 10 550 ...
2025-05-22 600 15 520 ...

Confluence для автоматических запусков 2 раза в неделю

Выводы

  • система отвечает примерному представлению нашего CTO
  • Созданы баги и эпики на улучшение производительности 
  • Созданы доп. задачи для проверки автоскейлинга и их производительности

Спасибо за площадку

Источники

PerfConf11. Mocow. Нагружаем веб-сокеты без боли

By vitalic gorodkov

PerfConf11. Mocow. Нагружаем веб-сокеты без боли

  • 77