IoT

MQTT

Лёгкий протокол обмена сообщениями по модели публикация-подписка поверх TCP, ставший стандартом де-факто для телеметрии IoT-устройств.

Что такое MQTT

MQTT — лёгкий протокол обмена сообщениями поверх TCP, построенный на модели publish/subscribe. Устройство не отправляет данные другому устройству напрямую: оно публикует сообщение в тему (topic) на брокере, а брокер рассылает его всем, кто на эту тему подписан. Отправитель не знает, кто и сколько получателей его читает, — и наоборот.

Расшифровка: исторически MQTT — MQ Telemetry Transport, где MQ пришло от продукта IBM MQ Series (часто встречается и вариант Message Queuing Telemetry Transport). Протокол создали в 1999 году Энди Стэнфорд-Кларк (IBM) и Арлен Ниппер для сбора телеметрии нефтепровода через спутниковый канал: отсюда требования — минимум служебного трафика и живучесть на нестабильной связи. В стандарте OASIS название закреплено просто как MQTT и аббревиатурой уже не считается.

Минимальный заголовок пакета — 2 байта. Именно поэтому MQTT вытеснил HTTP в IoT: на датчике, который шлёт число раз в минуту, заголовки HTTP-запроса весят больше самих данных.

Как устроен обмен

ESP32 (publisher)                  Брокер                    Дашборд (subscriber)
   |                                  |                              |
   |--- CONNECT --------------------->|<--- CONNECT -----------------|
   |<-- CONNACK ----------------------|---- CONNACK --------------->|
   |                                  |<--- SUBSCRIBE home/+/temp ---|
   |                                  |---- SUBACK ---------------->|
   |--- PUBLISH home/kitchen/temp --->|                              |
   |    payload: 21.5                 |---- PUBLISH --------------->|

Брокер — единственный обязательный сервер в схеме: он держит постоянные TCP-соединения со всеми клиентами и маршрутизирует сообщения по темам.

Темы (topics)

Тема — строка с уровнями через /, от общего к частному. Заранее создавать её не нужно: тема существует ровно в момент публикации.

factory/line-3/press-01/temperature
factory/line-3/press-01/state
home/kitchen/sensor-1/temperature
  • Регистр важен: Home/temp и home/temp — разные темы.
  • Ведущего / лучше избегать: /home/temp создаёт пустой первый уровень.
  • Темы вида $SYS/... зарезервированы под статистику самого брокера.

Подстановки работают только в подписке, публиковать в шаблон нельзя:

Шаблон Значение Пример
+ ровно один уровень home/+/temperature совпадёт с home/kitchen/temperature, но не с home/1/2/temperature
# все оставшиеся уровни, только в конце темы factory/# — всё поддерево цеха

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

QoS Гарантия Обмен пакетами Риск
0 at most once — отправил и забыл PUBLISH сообщение может потеряться
1 at least once PUBLISH → PUBACK возможны дубликаты
2 exactly once PUBLISH → PUBREC → PUBREL → PUBCOMP нет, но самый дорогой

Три момента, которые чаще всего понимают неверно:

  • QoS действует на одном участке, а не сквозь всю систему. Публикация издатель → брокер и доставка брокер → подписчик согласуются отдельно. Подписчик получит сообщение с QoS = min(QoS публикации, QoS подписки): опубликовав с QoS 2, но подписавшись с QoS 0, гарантий вы не получите.
  • QoS 1 требует идемпотентности. При потере PUBACK клиент повторит PUBLISH с флагом DUP, и обработчик увидит значение дважды. Для счётчика «прибавить 1» это ошибка, для «температура = 21.5» — нет.
  • QoS 2 стоит четырёх пакетов и задержки. Он нужен там, где дубль недопустим: команда управления, биллинговое событие.

На практике: частая телеметрия — QoS 0 (следующее значение придёт через секунду и всё равно перекроет потерянное), обычные измерения и события — QoS 1, команды и деньги — QoS 2.

Retained, LWT и keepalive

  • Retained-сообщение: брокер хранит последнее сообщение темы и отдаёт его новому подписчику сразу при подписке. Без этого дашборд после перезагрузки показывал бы пустые поля до следующего измерения. Retained-сообщение в теме одно — новое затирает старое.
  • LWT (Last Will and Testament): клиент при подключении регистрирует «завещание» — сообщение, которое брокер опубликует сам, если соединение оборвётся некорректно. Классика: home/kitchen/sensor-1/status со значением offline.
  • Keepalive: интервал, заявленный в CONNECT. Если клиент молчит дольше полутора интервалов, брокер считает его отключившимся и публикует LWT. Для поддержания тишины клиент шлёт PINGREQ.
  • Постоянная сессия: при clean session = false (в MQTT 5 — session expiry) брокер копит сообщения QoS 1–2 для отключившегося клиента и отдаёт их при возврате.

Практика: Mosquitto

# Брокер Eclipse Mosquitto в Docker со своим конфигом
docker run -d --name mosquitto -p 1883:1883 -p 8883:8883 \
  -v /srv/mosquitto/config:/mosquitto/config \
  -v /srv/mosquitto/data:/mosquitto/data \
  eclipse-mosquitto

# Подписаться на температуру всех комнат (-v печатает тему рядом со значением)
mosquitto_sub -h 192.168.1.10 -t 'home/+/temperature' -v

# Опубликовать значение с QoS 1 и флагом retain
mosquitto_pub -h 192.168.1.10 -t 'home/kitchen/temperature' -m '21.5' -q 1 -r

# Подписка на всё поддерево цеха через TLS
mosquitto_sub -h mqtt.example.com -p 8883 --cafile ca.crt \
  -u sensor01 -P secret -t 'factory/#' -v

Частая ошибка при первом запуске: начиная с версии 2.0 Mosquitto по умолчанию слушает только localhost и запрещает анонимные подключения. Для доступа извне нужен свой mosquitto.conf с явным listener 1883 и настроенной аутентификацией — «не подключается снаружи» почти всегда именно это, а не сеть.

MQTT 3.1.1 и MQTT 5

MQTT 5 обратно совместим по идеям, но заметно богаче:

  • Reason codes — брокер объясняет отказ, а не просто рвёт соединение (в 3.1.1 отладка прав доступа была гаданием).
  • Shared subscriptions ($share/группа/тема) — несколько подписчиков делят поток сообщений, то есть балансировка нагрузки средствами протокола.
  • Topic alias — длинная тема заменяется коротким числовым псевдонимом, экономия на каждом пакете.
  • Message expiry и session expiry — устаревшие сообщения не доставляются «из прошлого».
  • User properties — произвольные заголовки рядом с payload.

Брокеры и инфраструктура

Брокер Особенность
Eclipse Mosquitto лёгкий, один процесс на C; тянет одноплатник или роутер с OpenWrt
EMQX кластеризация и маршрутизация правилами, для крупных парков устройств
HiveMQ enterprise-вариант с кластером и расширениями
RabbitMQ MQTT через плагин — разумно, если AMQP уже используется

Порты: 1883 — открытый MQTT, 8883 — MQTT поверх TLS; оба закреплены за протоколом официально. Для браузерных дашбордов брокеры дополнительно отдают MQTT поверх WebSocket — порт зависит от брокера и задаётся в конфиге.

Брокер обычно живёт на VPS рядом с остальной платформой: он держит тысячи долгих TCP-соединений, поэтому важнее лимиты файловых дескрипторов и стабильность сети, чем процессор. Дальше поток часто уходит в Kafka и БД временных рядов вроде InfluxDB.

Связанные термины

  • IoT — область, ради которой протокол создавался
  • Брокеры сообщений — общий класс систем, к которому относится MQTT-брокер
  • TCP и UDP — транспорт под MQTT
  • TLS — шифрование MQTT на порту 8883
  • InfluxDB — куда обычно складывают принятую телеметрию
  • RabbitMQ — альтернативный брокер с поддержкой MQTT

Готовы запустить GPU-задачу?

Запустить GPU-сервер