Нагрузочное тестирование

Стресс-тесты брокера на двух стендах — по сети и по loopback — чтобы отделить свойства самого брокера от влияния окружения. Ниже методика, полные таблицы и короткие выводы.

Скачать отчёт в PDF

Стенды

Дата: 24 июля 2026 г. · Версия протокола: MQTT 3.1.1 · Инструмент: собственный нагрузочный клиент cmd/loadtest на кодеке проекта, без сторонних MQTT-библиотек.

Тестирование проведено на двух стендах, чтобы отделить свойства самого брокера от влияния окружения: сеть против loopback, слабая машина против мощной.

Стенд A — по сетиСтенд B — loopback
CPU брокера12 ядер2× Xeon E5-2690 v4 @ 2,6 ГГц (28 ядер / 56 потоков)
ОЗУ брокера16 ГБ128 ГБ
Адрес192.168.1.84:1883 (LAN)127.0.0.1:1883 (loopback)
Генератор нагрузкиОтдельная машина в сетиТа же машина, что и брокер
Эфемерных портов у клиента16 384 (по умолчанию)55 000 (расширено)
Как читать сравнение. Стенды не эквивалентны и отвечают на разные вопросы. Стенд A показывает работу по сети с выделенным генератором. Стенд B убирает сеть (задержка около нуля) и даёт брокеру мощное железо, но генератор и брокер делят одни и те же ядра. Поэтому задержку и fan-out честно сравнивать напрямую, а скорость приёма — нет: на стенде B клиент отбирает ядра у брокера.

Короткий вывод

  • Задержка. По сети сквозная задержка «издатель → брокер → подписчик» под штатной нагрузкой — около 1 мс; на loopback — 73 мкс (в ~16 раз ниже, сеть убрана).
  • Соединения. По сети брокер удержал 10 000 сессий без отказов (дальше упёрлись в порты клиента). На мощной машине по loopback — 30 000 без отказов и ~41 000 на попытке 50 000 (предел одной машины, а не брокера).
  • Приём. Пик по сети — ≈277 000 сообщений/с; на loopback приём ниже (≈185 000/с), потому что генератор конкурирует с брокером за ядра.
  • Fan-out (маршрутизация). На мощной машине брокер доставил ≈1 350 000 сообщений/с подписчикам (fan-out ×100) против ≈22 000/с по сети.
  • Устойчивость. Ни на одном стенде брокер не упал и не завис. Под перегрузкой для QoS 0 сообщения отстающим подписчикам штатно отбрасываются; лог брокера за все прогоны — без единой ошибки.

Ёмкость по одновременным подключениям

Каждое подключение — полный цикл CONNECT → CONNACK с аутентификацией; далее сессия удерживается открытой (с проверкой PINGREQ).

Стенд A — по сети

ЦельУстановленоОтказыУстановка p50p99
10100146 мс162 мс
1001000702 мс1,12 с
1 0001 00004,21 с9,78 с
5 0005 00004,64 с8,59 с
10 00010 00004,92 с10,52 с
15 00014 8791217,43 с14,09 с

Стенд B — loopback (плавный дозвон)

ЦельУстановленоОтказыУстановка p50p99
1 0001 0000886 мс1,06 с
10 00010 0000903 мс1,55 с
30 00030 0000923 мс1,90 с
50 00041 2190984 мс1,90 с

Разбор

  • По сети до 10 000 — без единого отказа; 121 отказ на 15 000 — исчерпание эфемерных портов клиента (16 384). Это предел генератора, а не брокера.
  • На loopback после расширения диапазона портов брокер удержал 30 000 сессий идеально; на 50 000 живыми к концу удержания остались ~41 000 — обе стороны делят одну машину. Лог брокера при этом чист, без ошибок.
  • Побочное наблюдение. На loopback слишком резкий залп подключений (1 000 одновременных dial) вызывает connection refused — переполняется очередь приёма (accept backlog): без сетевой задержки все SYN приходят разом. Плавный дозвон (по 200 одновременно) полностью снимает эффект. По сети задержка сглаживает залп сама.

Пропускная способность приёма

Пул издателей публикует максимально быстро; измеряется скорость приёма брокером.

QoS 0 · полезная нагрузка 64 Б

ИздателейСтенд A (сеть)Стенд B (loopback)
10271 006/с185 012/с
100236 422/с101 457/с
500103 546/с125 324/с

Приём по сети выше — не потому что брокер на стенде A быстрее, а потому что там генератор работает на отдельной машине. На loopback клиент и брокер делят одни ядра, и это ограничивает суммарную скорость.

QoS 1 (с подтверждением PUBACK) · 64 Б

ИздателейСтенд A (сеть)Стенд B (loopback)
1010 292/с63 606/с
10022 085/с48 983/с

Тестовый клиент QoS 1 синхронный (ждёт PUBACK после каждого сообщения), поэтому упирается в round-trip. На loopback round-trip почти нулевой — отсюда рост примерно в 6 раз. Это наглядно показывает: цифры QoS 1 определяются задержкой сети, а не брокером.

Зависимость от размера сообщения · QoS 0 · 50 издателей

РазмерСтенд A (сеть)Стенд B (loopback)
64 Б277 198/с · 17,7 МБ/с101 961/с · 6,5 МБ/с
256 Б124 997/с · 32,0 МБ/с102 029/с · 26,1 МБ/с
1 024 Б38 458/с · 39,4 МБ/с102 151/с · 104,6 МБ/с

Два разных потолка:

  • По сети упираемся в канал ≈40 МБ/с: с ростом сообщения число msg/с падает, а МБ/с держится у предела.
  • На loopback канал огромный, поэтому число сообщений держится ровно (~102 000/с при 50 соединениях), а объём растёт до 104,6 МБ/с. Здесь узкое место — обработка на сообщение (CPU), а не байты.

Сквозная доставка

Метка времени вкладывается в тело сообщения; замеряются реальная доставка и сквозная задержка. Fan-out — доставка каждому подписчику по фильтру.

Стенд A — по сети

СценарийПубликацияДоставкаavg / p50 / p99
10 подп. / 10 изд. / 100 сообщ./с — штатно986/с9 863/с1,17 / 1,0 / 3,6 мс
100 подп. / 10 изд. / без ограничения261 551/с22 351/с780 / 453 / 678 мс
100 подп. / 10 изд. / 500 сообщ./с4 934/с12 150/с1,87 / 1,77 / 2,83 с
1 000 подп. / 5 изд. / 100 сообщ./с494/с178 961/с2,29 с / 480 / 952 мс

Стенд B — loopback

СценарийПубликацияДоставкаavg / p50 / p99
10 подп. / 10 изд. / 100 сообщ./с — штатно987/с9 867/с73 / ~0 / 555 мкс
100 подп. / 10 изд. / без ограничения47 557/с1 353 431/с514 / 135 / 251 мс
100 подп. / 10 изд. / 500 сообщ./с3 608/с360 834/с17,1 / 10,5 / 71,6 мс
1 000 подп. / 5 изд. / 100 сообщ./с494/с183 746/с4,73 / 4,54 / 15,1 мс

Разбор

  • Задержка под штатной нагрузкой: 1,17 мс по сети против 73 мкс на loopback. Разница — это сетевой путь.
  • Маршрутизация под нагрузкой: мощная машина доставила ≈1,35 млн сообщений/с подписчикам (fan-out ×100) — на два порядка выше сетевого стенда (22 тыс./с), где мал и брокер, и канал.
  • В сценариях с большим fan-out узким местом также становится генератор (все подписчики в одном процессе на одной машине), поэтому пиковые цифры доставки — это нижняя оценка возможностей брокера.

Прямое сравнение

МетрикаСтенд A — сеть (12 ядер)Стенд B — loopback (28 ядер)
Соединений без отказов10 00030 000
Пик соединений14 879 (лимит клиента)~41 000 (лимит одной машины)
Приём QoS 0 (64 Б), пик271 006/с185 012/с
Приём QoS 1 (64 Б), пик22 085/с63 606/с
Потолок по объёму≈40 МБ/с (канал)≈105 МБ/с (CPU)
Задержка (штатная), медиана1,0 мс~0 (73 мкс avg)
Доставка при fan-out, пик22 351/с1 353 431/с
Ошибки в логе брокеранетнет

Ограничения методики

  • Скорость приёма между стендами напрямую не сравнима: на стенде B генератор и брокер делят одни ядра, на стенде A генератор вынесен на отдельную машину.
  • Нагрузка на каждом стенде подаётся с одной клиентской машины; в ряде сценариев (большой fan-out, десятки тысяч соединений) она, а не брокер, становится узким местом.
  • Тестовый клиент QoS 1 синхронный — цифры QoS 1 занижены относительно потенциала брокера.
  • Системные метрики брокера (CPU/RAM во время прогонов) не снимались; о состоянии сервера судим по его логу (ошибок нет) и по поведению клиента.

Как воспроизвести

Все прогоны запускаются одним бинарником loadtest; ниже команды для трёх режимов.

go build -o loadtest ./cmd/loadtest

# Ёмкость по подключениям (плавный дозвон против accept-backlog)
./loadtest -addr HOST:1883 -user myelx -pass *** \
    -mode conn -levels 1000,10000,30000,50000 -hold 3s -dial-concurrency 200

# Пропускная способность приёма
./loadtest -addr HOST:1883 -user myelx -pass *** \
    -mode pub -pubs 10 -qos 0 -payload 64 -duration 10s

# Сквозная доставка и задержка
./loadtest -addr HOST:1883 -user myelx -pass *** \
    -mode pubsub -subs 10 -pubs 10 -qos 0 -rate 100 -payload 64 -duration 10s