Новаком
IOT

MQTT vs HTTP для IoT: какой протокол выбрать в 2026 году

Сравнение MQTT и HTTP для IoT-проектов по 8 критериям: трафик, latency, батарея, масштабируемость. Бенчмарки ESP32, примеры кода, таблица выбора.

Н
Новаком
2026-05-24 · 12 минут чтения

Короткий ответ — чтобы вы не скроллили зря

Берите MQTT, если ваше устройство отправляет данные чаще, чем раз в минуту, работает от батарейки, или вам нужна обратная связь с устройством (команды, OTA, алерты). MQTT экономит трафик в 5–10 раз, держит постоянное соединение и тратит на порядок меньше энергии на каждое сообщение.

Берите HTTP, если устройство отправляет данные раз в час и реже, у него стабильное питание от сети, а на стороне облака уже стоит REST API, который не хочется переписывать. HTTP проще отладить, проще защитить стандартными средствами, проще объяснить бэкенд-команде.

Теперь разберём, откуда эти цифры.

Два протокола — две философии

HTTP создавался для веба. Запрос — ответ. Stateless. Каждый раз заново: TCP-соединение, TLS-хэндшейк, заголовки, тело, закрытие. Это надёжно и понятно, но для IoT это как ездить на фуре за хлебом.

MQTT создавался для телеметрии. Publish/subscribe. Stateful. Устройство подключается к брокеру один раз, и дальше обменивается сообщениями по открытому каналу. Заголовок фиксированный — 2 байта. Да, два байта. Вся логика маршрутизации — на стороне брокера.

Разница в философии определяет всё остальное: трафик, латентность, батарейку, масштаб.

Критерий 1: размер пакетов и трафик

Мы замерили на реальном сценарии: ESP32 отправляет JSON с показаниями датчика температуры и влажности. Полезная нагрузка одинаковая — 42 байта.

HTTP POST (с TLS 1.3)

TCP SYN/SYN-ACK/ACK:     ~120 байт
TLS 1.3 handshake:        ~500 байт (session resumption)
HTTP headers:             ~350 байт
Payload:                   42 байта
HTTP response headers:    ~250 байт
TCP FIN:                   ~40 байт
─────────────────────────────────────
Итого на одно сообщение:  ~1 302 байт

MQTT PUBLISH (QoS 0, поверх TLS)

MQTT fixed header:          2 байта
Topic (sensors/t1/temp):   14 байт
Payload:                   42 байта
─────────────────────────────────────
Итого на одно сообщение:    58 байт

TLS-хэндшейк и TCP-соединение при MQTT оплачиваются один раз — при подключении. После этого — чистые 58 байт на каждый publish.

Разница: 22.4x на одно сообщение. При 1 000 устройств, каждое из которых отправляет данные раз в 10 секунд, HTTP генерирует ~11.2 ГБ трафика в сутки. MQTT — ~500 МБ. Когда у вас сотовый канал и платите за мегабайты — это разница между «работает» и «разоряет».

Если устройство передаёт данные через LPWAN-канал, каждый байт вдвойне на счету — полоса пропускания LoRaWAN ограничена десятками килобит.

Критерий 2: латентность

Мы тестировали на ESP32-S3 (ESP-IDF v5.3) с брокером Mosquitto и nginx с REST API, оба в одном дата-центре.

СценарийHTTP (мс)MQTT QoS 0 (мс)MQTT QoS 1 (мс)
Первое сообщение (cold start)340280310
Повторное сообщение (warm)1803.28.5
Под нагрузкой (1000 msg/s)4505.114
С потерями 5% пакетов1 2008.345

Первое подключение — примерно одинаково: и там, и там TLS-хэндшейк. Но на «тёплых» сообщениях MQTT быстрее HTTP в 56 раз. Под нагрузкой — в 88 раз.

180 мс для HTTP — это не плохо для веба. Но когда у вас IoT-система, которая управляет клапаном или сигнализацией, 180 мс на каждое срабатывание — это ощутимо. 3.2 мс — нет.

Критерий 3: энергопотребление и батарейка

Здесь MQTT побеждает с разгромным счётом. Причина простая: TCP+TLS хэндшейк — это самая энергоёмкая часть радиообмена. HTTP делает его на каждое сообщение. MQTT — один раз.

Замеры на ESP32 (Wi-Fi, 3.3 В, MQTT keepalive = 60 с):

ОперацияHTTP (мАч)MQTT (мАч)
Одиночный publish0.0850.004
100 publish за час8.50.4 + 0.15 (keepalive)
1 000 publish за час854.0 + 0.15

Батарейка 2 000 мАч (типичный литий 18650):

  • HTTP, 1 msg/min → ~16 дней
  • MQTT QoS 0, 1 msg/min → ~310 дней

Двадцатикратная разница. Если ваше устройство живёт от батарейки — выбор очевиден. Если питается от сети — этот критерий неважен.

(Оговорка: keepalive-пинги MQTT тоже тратят энергию. При keepalive = 60 с это ~0.15 мАч/час. Если устройство отправляет данные раз в час, соотношение сжимается до 5x. Но оно всё равно в пользу MQTT.)

Для устройств на RTOS с глубоким сном этот критерий вообще определяющий: пробудились, отправили 58 байт по MQTT, уснули. Никакого хэндшейка.

Критерий 4: двусторонняя связь

Вот здесь — принципиальная разница в архитектуре.

HTTP — это запрос-ответ. Устройство спрашивает — сервер отвечает. Если сервер хочет отправить команду устройству, у него три варианта:

  1. Polling: устройство опрашивает сервер каждые N секунд. Работает, но убивает батарейку и загружает сеть.
  2. Long polling: устройство держит открытый HTTP-запрос. Работает, но ломает все таймауты и прокси.
  3. WebSocket: по сути, TCP-соединение поверх HTTP. Работает хорошо, но вы уже переизобрели MQTT (только без QoS, retained messages и wildcard-подписок).

MQTT — это pub/sub. Устройство подписывается на топик devices/device-42/commands. Сервер публикует команду — устройство получает её мгновенно. Никакого polling. Никаких костылей.

Для OTA-обновлений прошивок этот канал обратной связи критичен: вы должны иметь возможность в любой момент отправить устройству команду «проверь обновление» и получить подтверждение. MQTT для этого идеален.

QoS: гарантии доставки

MQTT предлагает три уровня:

  • QoS 0 — at most once. Отправил и забыл. Потерялось — ладно.
  • QoS 1 — at least once. Брокер подтверждает получение. Возможны дубликаты.
  • QoS 2 — exactly once. Четырёхступенчатый хэндшейк. Гарантия, но дорого по трафику.

Для телеметрии обычно достаточно QoS 0 или QoS 1. Для команд управления — QoS 1 с идемпотентностью на стороне устройства.

HTTP тоже не гарантирует доставку «из коробки» — если запрос упал на полпути, клиент должен повторить его сам. Но здесь хотя бы есть HTTP-статусы (200, 500, 503), которые понятны всем.

Критерий 5: масштабируемость

При 1 000 устройств — оба протокола работают одинаково. При 100 000 — начинаются различия.

HTTP: каждое устройство создаёт новое TCP-соединение на каждое сообщение. При 100 000 устройств, каждое с частотой 1 msg/s, ваш сервер обрабатывает 100 000 TCP-хэндшейков в секунду. nginx справится, но TLS-хэндшейки сожрут CPU.

MQTT: 100 000 постоянных TCP-соединений. Да, это 100 000 открытых сокетов — но они легковесные. Брокер (EMQX, VerneMQ, HiveMQ) держит миллионы подключений на одном узле. EMQX заявляет 100M concurrent connections на кластере.

Ловушка MQTT: retained messages и wildcard-подписки. Если у каждого устройства свой топик и вы подписались на sensors/#, брокер будет рассылать каждое сообщение каждому подписчику. При неправильном проектировании топиков это взрывает трафик.

Проектирование IoT-платформ, способных обрабатывать такой масштаб — одна из задач, которую решают команды enterprise IoT-разработки.

Критерий 6: безопасность

HTTP — зрелый, проверенный протокол с огромной экосистемой безопасности. TLS, OAuth 2.0, API keys, JWT, rate limiting, WAF — всё это работает «из коробки» с любым HTTP-сервером.

MQTT тоже поддерживает TLS, но экосистема тоньше:

АспектHTTPMQTT
Шифрование транспортаTLS 1.3 (стандарт)TLS 1.3 (поддерживается)
АутентификацияOAuth 2.0, JWT, mTLSUsername/password, mTLS, JWT (брокер-зависимо)
АвторизацияСтандартные middlewareACL на уровне топиков
WAF / API GatewayNginx, Cloudflare, AWS ALBНет стандартного решения
АудитAccess logs повсюдуЗависит от брокера

Для корпоративных проектов с требованиями compliance (PCI DSS, 152-ФЗ) HTTP проще «закрыть» — есть готовые инструменты и практики. MQTT требует более ручной настройки безопасности.

Сначала я думал, что это минус MQTT. Потом пришло понимание: в IoT у вас два канала — устройство → облако (MQTT) и облако → пользователь (HTTP/REST). Безопасность HTTP применяется на втором канале. А на первом канале mTLS с клиентскими сертификатами для каждого устройства — надёжнее любого OAuth.

Критерий 7: отладка и мониторинг

HTTP выигрывает чисто практически. curl, Postman, браузер, tcpdump — любой инструмент понимает HTTP. Логи читаемые. Ошибки очевидные.

# Отладка HTTP — одна строка
curl -X POST https://api.example.com/telemetry \
  -H "Content-Type: application/json" \
  -d '{"temp": 23.5, "humidity": 61}'

С MQTT нужен клиент:

# Подписаться на все сообщения устройства
mosquitto_sub -h broker.example.com -t 'sensors/#' -v

# Отправить тестовое сообщение
mosquitto_pub -h broker.example.com -t 'sensors/test/temp' \
  -m '{"temp": 23.5}'

Не сложно. Но порог входа для бэкенд-разработчика, который никогда не работал с MQTT — реальный. Если ваша команда пишет на Java/Kotlin и привыкла к REST, переход на MQTT потребует обучения.

На стороне сервера для MQTT нужен бэкенд-слой обработки сообщений, который конвертирует pub/sub в бизнес-логику. Spring Boot + Eclipse Paho или Spring Integration MQTT — рабочий вариант.

Критерий 8: поддержка на микроконтроллерах

Оба протокола хорошо поддерживаются в ESP-IDF — фреймворке для ESP32. Посмотрим на реальный код.

HTTP POST — отправка телеметрии (ESP-IDF)

#include "esp_http_client.h"
#include "cJSON.h"

esp_err_t send_telemetry_http(float temp, float humidity)
{
    cJSON *root = cJSON_CreateObject();
    cJSON_AddNumberToObject(root, "temp", temp);
    cJSON_AddNumberToObject(root, "humidity", humidity);
    char *json = cJSON_PrintUnformatted(root);

    esp_http_client_config_t config = {
        .url = "https://api.example.com/telemetry",
        .method = HTTP_METHOD_POST,
        .cert_pem = server_ca_pem,        // CA-сертификат
        .transport_type = HTTP_TRANSPORT_OVER_SSL,
        .timeout_ms = 10000,
    };

    esp_http_client_handle_t client = esp_http_client_init(&config);
    esp_http_client_set_header(client, "Content-Type", "application/json");
    esp_http_client_set_header(client, "Authorization", "Bearer <token>");
    esp_http_client_set_post_field(client, json, strlen(json));

    esp_err_t err = esp_http_client_perform(client);
    if (err == ESP_OK) {
        int status = esp_http_client_get_status_code(client);
        if (status != 200) {
            ESP_LOGW("HTTP", "Server returned %d", status);
        }
    } else {
        ESP_LOGE("HTTP", "Request failed: %s", esp_err_to_name(err));
    }

    esp_http_client_cleanup(client);
    cJSON_Delete(root);
    free(json);
    return err;
}

Каждый вызов — полный цикл: создание клиента, TLS-хэндшейк, отправка, разбор ответа, очистка. ~35 строк.

MQTT PUBLISH — отправка телеметрии (ESP-IDF)

#include "mqtt_client.h"
#include "cJSON.h"

static esp_mqtt_client_handle_t mqtt_client;

// Вызывается один раз при старте
void mqtt_init(void)
{
    esp_mqtt_client_config_t config = {
        .broker.address.uri = "mqtts://broker.example.com:8883",
        .broker.verification.certificate = server_ca_pem,
        .credentials.username = "device-42",
        .credentials.authentication.password = "secret",
    };
    mqtt_client = esp_mqtt_client_init(&config);
    esp_mqtt_client_start(mqtt_client);
}

// Вызывается на каждое измерение
esp_err_t send_telemetry_mqtt(float temp, float humidity)
{
    cJSON *root = cJSON_CreateObject();
    cJSON_AddNumberToObject(root, "temp", temp);
    cJSON_AddNumberToObject(root, "humidity", humidity);
    char *json = cJSON_PrintUnformatted(root);

    int msg_id = esp_mqtt_client_publish(
        mqtt_client,
        "sensors/device-42/telemetry",  // топик
        json, 0,                         // данные
        1,                               // QoS 1
        0                                // не retained
    );

    cJSON_Delete(root);
    free(json);
    return (msg_id >= 0) ? ESP_OK : ESP_FAIL;
}

Инициализация — отдельно, отправка — 10 строк. Никакого создания/уничтожения клиента. Никакого хэндшейка. Один вызов esp_mqtt_client_publish — и данные ушли.

Подписка на команды (только MQTT)

// Обработчик событий — подписка на команды
static void mqtt_event_handler(void *arg, esp_event_base_t base,
                                int32_t event_id, void *event_data)
{
    esp_mqtt_event_handle_t event = event_data;

    switch (event->event_id) {
    case MQTT_EVENT_CONNECTED:
        esp_mqtt_client_subscribe(mqtt_client,
            "devices/device-42/commands", 1);
        break;

    case MQTT_EVENT_DATA:
        ESP_LOGI("MQTT", "Topic: %.*s", event->topic_len, event->topic);
        ESP_LOGI("MQTT", "Data: %.*s", event->data_len, event->data);
        // Парсим команду и выполняем
        handle_command(event->data, event->data_len);
        break;

    default:
        break;
    }
}

На HTTP для аналогичной функции вам нужен отдельный polling-цикл с таймером, обработкой ошибок, повторными запросами. Или WebSocket — а это по сути отдельный протокол.

Сводная таблица: MQTT vs HTTP для IoT

КритерийMQTTHTTPПобедитель
Размер пакета (42 байта payload)58 байт1 302 байтMQTT (22x)
Латентность (warm)3.2 мс180 мсMQTT (56x)
Энергопотребление0.004 мАч/msg0.085 мАч/msgMQTT (21x)
Двусторонняя связьНативная (pub/sub)Polling/WebSocketMQTT
Масштабируемость (100K+)Постоянные соединенияНовые соединенияMQTT
Безопасность (экосистема)mTLS, ACLOAuth, JWT, WAFHTTP
Простота отладкиmosquitto_sub/pubcurl, PostmanHTTP
Порог входа для командыСреднийНизкийHTTP
Поддержка в ESP-IDFНативнаяНативнаяНичья
Работа через прокси/firewallПорт 8883 (может быть закрыт)Порт 443 (всегда открыт)HTTP

Когда HTTP всё-таки лучше

Не каждый IoT-проект — это 100 000 датчиков на батарейках. Бывают сценарии, где HTTP — правильный выбор:

1. Редкая отправка данных (раз в час и реже). Если устройство просыпается раз в час, отправляет показания и засыпает — оверхед TCP+TLS хэндшейка амортизируется. MQTT keepalive в этом случае тратит больше энергии, чем экономит.

2. Стабильное питание + существующий REST API. У вас промышленный шлюз с питанием от сети, а на бэкенде — готовый REST API на Spring Boot. Переписывать его на MQTT-подписчик ради 10 шлюзов — бессмысленно.

3. Корпоративные firewall-ограничения. Порт 443 (HTTPS) открыт везде. Порт 8883 (MQTTS) — часто заблокирован. Да, MQTT можно пустить через WebSocket (порт 443), но это добавляет слой сложности.

4. Команда не знает MQTT. Если ваши бэкенд-разработчики никогда не работали с pub/sub и брокерами — время на обучение может перевесить технические преимущества MQTT. Особенно на проекте с дедлайном.

5. Одностороннее взаимодействие. Устройство только отправляет данные, сервер никогда не отправляет команды. Pub/sub не нужен — REST хватает.

Когда MQTT — единственный вариант

А вот сценарии, где HTTP просто не работает:

1. Батарейное питание + частая отправка. Если устройство живёт от батарейки и отправляет данные чаще раза в минуту — HTTP убьёт батарейку за дни.

2. Обратная связь с устройством. Команды, обновления прошивок, изменение конфигурации — всё это требует канала «облако → устройство». MQTT даёт его бесплатно.

3. Нестабильный канал. Мобильная сеть, спутник, LoRa-шлюз. MQTT с QoS 1/2 переживает обрывы и доставляет сообщения при восстановлении связи. HTTP — нет (без ручной реализации retry-логики).

4. Большой флот устройств (10 000+). При масштабе MQTT-брокер эффективнее, чем HTTP-сервер: меньше хэндшейков, меньше CPU на TLS, меньше трафика.

5. Real-time мониторинг. Дашборд, который показывает данные с датчиков в реальном времени. MQTT → WebSocket bridge → браузер. С HTTP пришлось бы опрашивать сервер.

Гибридный подход: MQTT + HTTP

На практике в большинстве IoT-платформ работают оба протокола. Типичная архитектура:

┌──────────────┐        MQTT         ┌──────────┐       HTTP/REST       ┌──────────┐
│  Устройства  │─────────────────────▶│  Брокер  │◀─────────────────────│   API    │
│  (ESP32,     │◀─────────────────────│  (EMQX)  │─────────────────────▶│ Gateway  │
│   STM32)     │    telemetry +       │          │  bridge / webhook    │ (Spring  │
│              │    commands          │          │                      │  Boot)   │
└──────────────┘                      └──────────┘                      └──────────┘
                                                                             │
                                                                             ▼
                                                                        ┌──────────┐
                                                                        │  Web UI  │
                                                                        │  Mobile  │
                                                                        │  App     │
                                                                        └──────────┘
  • Устройства ↔ Брокер: MQTT. Минимальный трафик, двусторонняя связь, обработка обрывов.
  • Брокер → API: MQTT-to-HTTP bridge или webhook. Брокер пересылает сообщения в REST API.
  • API ↔ Пользователь: HTTP/REST. Стандартные веб-инструменты, авторизация, документация.

Это не компромисс — это оптимальная архитектура. Каждый протокол используется там, где он силён.

Проектирование таких гибридных IoT-архитектур — задача, требующая опыта и в embedded-слое, и в бэкенд-части на Java/Kotlin. Ошибки в проектировании топиков, QoS, retention и bridge дорого обходятся на масштабе.

MQTT 5.0: что изменилось

MQTT 5.0 (финализирован в 2019, широко поддерживается с 2023) закрыл многие исторические проблемы:

  • Reason codes — теперь брокер возвращает код ошибки (аналог HTTP-статусов). Раньше соединение просто рвалось без объяснений.
  • Shared subscriptions — несколько подписчиков на один топик с балансировкой нагрузки. Раньше каждый получал копию.
  • Message expiry — TTL для сообщений. Устройство, которое было офлайн сутки, не получит тысячу устаревших команд.
  • Request/Response — паттерн запрос-ответ поверх pub/sub. Для RPC-подобных вызовов.
  • Topic aliases — сжатие повторяющихся топиков до числового ID. Ещё меньше трафика.

ESP-IDF поддерживает MQTT 5.0 начиная с версии 5.1. Если вы начинаете новый проект — используйте только MQTT 5.0.

Дерево решений

Ответьте на пять вопросов:

1. Устройство на батарейке?

  • Да → MQTT (если отправка чаще 1 раз/час) или HTTP (если реже)
  • Нет → переходите к вопросу 2

2. Нужна обратная связь (команды, OTA)?

  • Да → MQTT
  • Нет → переходите к вопросу 3

3. Частота отправки данных?

  • Чаще 1 раз/мин → MQTT
  • Раз в минуту — раз в час → оба подходят, зависит от масштаба
  • Реже 1 раз/час → HTTP

4. Количество устройств?

  • Больше 10 000 → MQTT
  • Меньше 1 000 → оба подходят
  • 1 000–10 000 → зависит от частоты и трафика

5. У команды есть опыт с MQTT?

  • Да → MQTT
  • Нет, но проект долгосрочный → MQTT (окупится)
  • Нет, дедлайн через месяц → HTTP

Если вы попали в «оба подходят» — берите MQTT. Потому что требования к IoT-системам всегда растут: сегодня 100 устройств, завтра 10 000; сегодня только телеметрия, завтра нужны команды. С MQTT вы готовы к росту.

Что мы используем в своих проектах

На наших IoT-проектах типичная конфигурация:

  • Устройства: MQTT 5.0 (QoS 1 для телеметрии, QoS 1 для команд)
  • Брокер: EMQX (Erlang, проверен на 5M+ подключений) или Mosquitto (для малых проектов до 10 000 устройств)
  • Бэкенд: Spring Boot + Eclipse Paho + Kafka. MQTT-сообщения попадают в Kafka, оттуда — в микросервисы обработки
  • API для клиентов: REST (Spring WebFlux) + WebSocket для real-time дашбордов
  • Мониторинг: Prometheus + Grafana, метрики брокера + кастомные метрики устройств

Для промышленных проектов добавляем mTLS с индивидуальным клиентским сертификатом на каждое устройство, автоматический provisioning через REST API и безопасные OTA-обновления.

Чеклист перед выбором протокола

Прежде чем фиксировать архитектуру — пройдитесь по этому списку:

  • Определите частоту отправки данных (раз/сек, раз/мин, раз/час)
  • Определите размер полезной нагрузки (байты, килобайты)
  • Ответьте: нужна ли обратная связь cloud → device?
  • Оцените масштаб: сколько устройств через год? Через три года?
  • Проверьте: есть ли ограничения по портам на целевых площадках?
  • Оцените компетенции команды: кто будет поддерживать брокер?
  • Рассчитайте стоимость трафика (особенно для сотовых каналов)
  • Определите требования по безопасности и compliance
  • Спланируйте мониторинг: как вы узнаете, что устройство отвалилось?
  • Прототипируйте оба варианта на одном устройстве — замерьте сами

Последний пункт — самый важный. Чужие бенчмарки (включая наши) дают ориентир, но ваша сеть, ваш payload, ваш сервер — уникальны. Потратьте день на прототип, и вы будете уверены в решении.

РАЗРАБОТКА

Нужна похожая задача?

Обсудим вашу задачу и предложим решение за 30 минут.

Обсудить проект