Стенды
Дата: 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 (расширено) |
Короткий вывод
- Задержка. По сети сквозная задержка «издатель → брокер → подписчик» под штатной нагрузкой — около 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 — по сети
| Цель | Установлено | Отказы | Установка p50 | p99 |
|---|---|---|---|---|
| 10 | 10 | 0 | 146 мс | 162 мс |
| 100 | 100 | 0 | 702 мс | 1,12 с |
| 1 000 | 1 000 | 0 | 4,21 с | 9,78 с |
| 5 000 | 5 000 | 0 | 4,64 с | 8,59 с |
| 10 000 | 10 000 | 0 | 4,92 с | 10,52 с |
| 15 000 | 14 879 | 121 | 7,43 с | 14,09 с |
Стенд B — loopback (плавный дозвон)
| Цель | Установлено | Отказы | Установка p50 | p99 |
|---|---|---|---|---|
| 1 000 | 1 000 | 0 | 886 мс | 1,06 с |
| 10 000 | 10 000 | 0 | 903 мс | 1,55 с |
| 30 000 | 30 000 | 0 | 923 мс | 1,90 с |
| 50 000 | 41 219 | 0 | 984 мс | 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) |
|---|---|---|
| 10 | 271 006/с | 185 012/с |
| 100 | 236 422/с | 101 457/с |
| 500 | 103 546/с | 125 324/с |
Приём по сети выше — не потому что брокер на стенде A быстрее, а потому что там генератор работает на отдельной машине. На loopback клиент и брокер делят одни ядра, и это ограничивает суммарную скорость.
QoS 1 (с подтверждением PUBACK) · 64 Б
| Издателей | Стенд A (сеть) | Стенд B (loopback) |
|---|---|---|
| 10 | 10 292/с | 63 606/с |
| 100 | 22 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 000 | 30 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