Основы MQTT: Publish/Subscribe, брокер, топики, QoS
Введение в MQTT и модель Publish/Subscribe
MQTT (Message Queuing Telemetry Transport) — это легковесный сетевой протокол обмена сообщениями, работающий поверх TCP/IP. Он спроектирован для использования в условиях ограниченной пропускной способности сети и на устройствах с малым объемом вычислительных ресурсов. Эти характеристики делают его идеальным решением для межмашинного взаимодействия (M2M) и Интернета Вещей (IoT), где тысячи датчиков и исполнительных устройств должны обмениваться данными надежно и эффективно. На контроллере платформы HI протокол MQTT является одной из ключевых технологий для построения распределенных систем автоматизации.> ℹ️ Информация: MQTT был разработан инженерами IBM в конце 90-х годов для мониторинга состояния нефтепроводов в реальном времени. Задача стояла в том, чтобы надежно передавать небольшие объемы телеметрических данных по спутниковым каналам связи, которые отличались высокой стоимостью трафика и нестабильностью. Именно эти жесткие требования и определили ключевые преимущества протокола, которые сегодня востребованы в IoT.
Основное отличие MQTT от традиционных протоколов, таких как HTTP, заключается в его архитектурной модели — «Издатель/Подписчик» (Publish/Subscribe, или Pub/Sub). Давайте сравним ее с более привычной моделью «клиент-сервер».
В модели «клиент-сервер»:
В модели «Издатель/Подписчик» (MQTT):
Таким образом, издатели и подписчики полностью декаплированы (развязаны) друг от друга. Датчик температуры не имеет ни малейшего представления о том, кто использует его показания — панель визуализации, система отопления или облачный сервис для аналитики. Аналогично, система отопления не знает, какой именно датчик предоставляет ей данные о температуре. Эта развязка обеспечивается брокером.
Преимущества модели Publish/Subscribe и протокола MQTT
Использование такой архитектуры дает ряд фундаментальных преимуществ, критически важных для систем автоматизации:
- Масштабируемость: Вы можете добавлять в систему сотни новых датчиков (издателей) или систем управления (подписчиков), не внося никаких изменений в уже существующие компоненты.
- Гибкость: Можно легко заменить один датчик на другой, если он публикует данные в том же формате. Системы-подписчики даже не заметят этой замены.
- Низкие накладные расходы: Заголовок MQTT-сообщения может занимать всего 2 байта, что делает его чрезвычайно эффективным по сравнению с громоздкими HTTP-заголовками. Это снижает нагрузку на сеть и экономит трафик, что особенно важно при использовании GSM или LoRaWAN модулей.
- Работа в нестабильных сетях: MQTT имеет встроенные механизмы (QoS и LWT, которые мы рассмотрим далее), позволяющие гарантировать доставку сообщений и отслеживать состояние устройств даже при временных обрывах связи.
- Низкое энергопотребление: Простота протокола требует меньше вычислительных ресурсов для обработки, что делает его идеальным для устройств с батарейным питанием.
Для контроллеров HI, MQTT является нервной системой проекта, связывающей воедино логику Node-RED, физические входы/выходы, стороннее оборудование (например, по Modbus), пользовательские интерфейсы и облачные платформы.
---
Архитектура MQTT: Брокер и Топики
Теперь, когда мы поняли концепцию Pub/Sub, давайте детальнее разберем два ее ключевых элемента: брокера и механизм маршрутизации сообщений — топики.
Роль MQTT-Брокера
Брокер MQTT — это центральный сервер, который управляет всем процессом обмена сообщениями. Его можно представить как "почтовое отделение" вашей системы автоматизации.Его основные задачи:
На борту контроллера HI по умолчанию установлен и настроен один из самых популярных и надежных брокеров — Mosquitto. Это означает, что для организации обмена данными внутри одного объекта вам не требуется дополнительное серверное оборудование. Все клиенты — будь то потоки Node-RED, датчики, мобильные приложения или другие контроллеры в локальной сети — подключаются к этому встроенному брокеру.
Топики: Адресная система MQTT
Если брокер — это почтовое отделение, то топики (topics) — это почтовые адреса. Топик представляет собой строку в формате UTF-8, которая определяет "канал" или "категорию" для сообщения. Именно на основе топиков брокер решает, кому доставить сообщение.
Топики имеют иерархическую структуру, уровни которой разделяются косой чертой (`/`). Это позволяет создавать логичную и понятную структуру данных в проекте.
> 💡 Подсказка: Проектируйте структуру топиков заранее. Хорошая иерархия — залог масштабируемой и легко обслуживаемой системы. Рекомендуемая практика: `проект/местоположение/устройство/параметр`. Например: `my-home/floor1/living-room/light-group1/state`.
Рассмотрим хороший пример структуры топиков для умного дома:
- `my-home/floor1/living-room/temperature` — телеметрия с датчика температуры в гостиной.
- `my-home/floor1/living-room/light-main/command` — команда на управление основной группой света.
- `my-home/floor1/living-room/light-main/state` — текущее состояние этой группы света (обратная связь).
- `my-home/security/main-door/status` — статус входной двери (открыта/закрыта).
Издатель, отправляя сообщение, указывает топик. Например, датчик температуры публикует значение `23.5` в топик `my-home/floor1/living-room/temperature`. Подписчик (например, Node-RED flow, управляющий кондиционером) подписывается на этот топик и немедленно получает сообщение, как только оно поступает на брокер.
Шаблоны подписки (Wildcards)
MQTT предоставляет мощный механизм для подписки сразу на несколько топиков с помощью шаблонов (wildcards). Их всего два:
- `+` (плюс) — одноуровневый шаблон. Заменяет ровно один уровень в иерархии топиков.
- `#` (решетка) — многоуровневый шаблон. Заменяет ноль или более уровней в иерархии. Должен быть последним символом в строке топика.
| Шаблон подписки | Примеры совпадающих топиков | Примеры несовпадающих топиков |
| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ |
| `my-home/floor1/+/temperature` | `my-home/floor1/living-room/temperature`
`my-home/floor1/kitchen/temperature` | `my-home/floor2/bedroom/temperature`
`my-home/floor1/humidity` |
| `my-home/+/living-room/light-main/#` | `my-home/floor1/living-room/light-main/state`
`my-home/floor1/living-room/light-main/command`
`my-home/floor2/living-room/light-main/state` | `my-home/floor1/kitchen/light/state` |
| `my-home/security/#` | `my-home/security/main-door/status`
`my-home/security/garage/motion-sensor`
`my-home/security/alarm/state` | `my-home/floor1/temperature` |
Использование шаблонов позволяет создавать гибкие системы мониторинга и логирования. Например, можно создать один поток в Node-RED, который подписывается на `my-home/#` и записывает абсолютно все сообщения системы в базу данных MySQL для последующего анализа.
---
Практика: Публикация и подписка в Node-RED
Теория важна, но лучший способ понять MQTT — это увидеть его в действии. В этом разделе мы создадим два простых потока в Node-RED на контроллере HI для отправки и получения сообщений через локальный MQTT-брокер.
Обзор узлов `mqtt in` и `mqtt out`
В стандартной палитре Node-RED есть два основных узла для работы с MQTT:
- `mqtt out`: Издатель (Publisher). Этот узел подключается к брокеру и публикует `msg.payload` в указанный топик.
- `mqtt in`: Подписчик (Subscriber). Этот узел подписывается на указанный топик (или шаблон) на брокере и создает новое сообщение `msg` каждый раз, когда получает публикацию.
Шаг 1: Настройка подключения к MQTT-брокеру
Прежде чем использовать узлы, нам нужно настроить подключение к брокеру. Эта настройка создается один раз и затем используется во всех MQTT-узлах.
* Name: `HI Local Broker` (или любое понятное вам имя).
* Server: `localhost` (можно также использовать `127.0.0.1`). Это указывает, что брокер запущен на самом контроллере.
* Port: `1883`. Это стандартный порт для MQTT без шифрования.
Шаг 2: Создание потока для публикации данных (Publisher)
Создадим поток, который по нажатию кнопки будет отправлять JSON-объект в MQTT.
* Payload: выберите тип `JSON`.
* В редактор JSON вставьте объект, соответствующий нашему стандартному контракту сообщений (как было рассмотрено в предыдущих уроках):
{
"value": true,
"source": "manual-test-button",
"ts": 1678886400000
}
> 💡 Подсказка: Вместо статичного `ts` можно использовать `timestamp` из узла `Inject`, который затем преобразуется в нужный формат в узле `Function`. Для нашего примера статического значения достаточно.
* Topic: `home/test/light/command`
* Server: Убедитесь, что выбран `HI Local Broker @ localhost:1883`.
* Topic: Оставьте это поле пустым. В этом случае узел будет использовать топик, заданный в `msg.topic` входящего сообщения. Это очень гибкая практика.
* QoS: Оставьте `0 - at most once`.
* Retain: Оставьте `false`.
* Name: "Publish Test Command"
Шаг 3: Создание потока для подписки (Subscriber)
Теперь создадим поток, который будет слушать тот же топик и выводить полученные сообщения в панель отладки.
* Server: Выберите тот же `HI Local Broker @ localhost:1883`.
* Topic: `home/test/light/command`.
* QoS: `0 - at most once`.
* Output: `a parsed JSON object`. Это очень удобная опция, которая автоматически преобразует входящую JSON-строку в JavaScript-объект в `msg.payload`.
* Name: "Subscribe to Test Commands".
* Output: выберите `complete msg object`, чтобы видеть не только `payload`, но и `topic`, `qos` и другие свойства.
Шаг 4: Тестирование
Ваш холст теперь должен выглядеть примерно так:
// Поток публикации
[Inject] ---> [mqtt out: "Publish Test Command"]
// Поток подписки
[mqtt in: "Subscribe to Test Commands"] ---> [Debug]
В панели отладки вы мгновенно увидите новое сообщение. Оно будет выглядеть примерно так:
{
"topic": "home/test/light/command",
"payload": {
"value": true,
"source": "manual-test-button",
"ts": 1678886400000
},
"qos": 0,
"retain": false,
"_msgid": "..."
}
Поздравляем! Вы только что реализовали полный цикл Publish/Subscribe. Узел `Inject` выступил в роли издателя, а узел `Debug` через `mqtt in` — в роли подписчика. Их коммуникация произошла асинхронно через брокер.
---
Уровни качества обслуживания (QoS) и LWT
Протокол MQTT был создан для работы в ненадежных сетях, поэтому в нем заложены механизмы, позволяющие управлять гарантией доставки сообщений. Эти механизмы называются уровнями качества обслуживания (Quality of Service, QoS).
Выбор правильного QoS — это всегда компромисс между надежностью и накладными расходами (трафиком и загрузкой процессора).
Три уровня QoS
- QoS 0: At most once (Не более одного раза)
* Гарантия: Нет гарантии доставки. Сообщение может быть потеряно, если произойдет разрыв соединения.
* Накладные расходы: Минимальные. Самый быстрый уровень.
* Когда использовать: Для частых, но не критически важных данных, потеря которых не страшна. Например, телеметрия с датчика температуры, отправляемая каждые 5 секунд. Если одно значение потеряется, через 5 секунд придет новое.
- QoS 1: At least once (Не менее одного раза)
* Гарантия: Сообщение будет доставлено как минимум один раз. В редких случаях (если `PUBACK` потерялся, но сообщение было доставлено) оно может быть доставлено повторно.
* Накладные расходы: Средние. Требуется дополнительный обмен пакетами.
* Когда использовать: Для команд и важных событий, потеря которых недопустима. Например, команда на включение реле света или открытие клапана воды. Повторное получение команды "включить" обычно не является проблемой. Это самый распространенный уровень QoS в системах автоматизации.
- QoS 2: Exactly once (Ровно один раз)
* Гарантия: Сообщение будет доставлено ровно один раз.
* Накладные расходы: Максимальные. Самый медленный и ресурсоемкий уровень.
* Когда использовать: Только в самых критически важных системах, где дублирование сообщений так же недопустимо, как и их потеря. Например, в системах биллинга или финансовых транзакциях. В 99% задач умного дома или промышленной автоматизации этот уровень избыточен.
> ⚠️ Внимание: Использование QoS 2 создает значительную нагрузку на сеть, процессор контроллера и брокер. Применяйте его только после тщательного анализа и в исключительных случаях, когда недопустима ни потеря, ни дублирование сообщений.
Последнее завещание (Last Will and Testament, LWT)
LWT — это мощная функция MQTT, позволяющая клиенту заранее определить сообщение, которое брокер опубликует от его имени в случае некорректного разрыва соединения (например, обрыв кабеля, сбой питания устройства, зависание ПО).Как это работает:
* LWT Topic: `devices/pump-station-1/status`
* LWT Message: `offline`
* LWT QoS: `1`
Основной диспетчерский поток в Node-RED может быть подписан на топик `devices/+/status`. Получив сообщение `offline`, он может немедленно запустить аварийный сценарий: отправить уведомление оператору, переключиться на резервное оборудование и т.д. Таким образом, LWT является фундаментальным инструментом для построения отказоустойчивых систем и мониторинга доступности всех компонентов вашей IoT-инфраструктуры.
---
Резюме и выводы
В этом уроке мы заложили фундамент для работы с протоколом MQTT, который является основой для построения современных распределенных систем автоматизации на платформе HI.
> 📋 Ключевые понятия:
> * MQTT: Легковесный протокол для M2M/IoT.
> * Publish/Subscribe: Асинхронная модель обмена данными, decoupled.
> * Брокер: Центральный сервер, маршрутизирующий сообщения.
> * Клиент: Любое устройство или ПО, подключающееся к брокеру.
> * Топик: Иерархический адрес для сообщений.
> * QoS: Уровень гарантии доставки сообщения.
> * LWT: Механизм для отслеживания нештатного отключения клиентов.
Ключевой вывод заключается в том, что MQTT — это не просто протокол, а архитектурный паттерн. Он идеально подходит для событийно-ориентированной среды Node-RED, позволяя создавать гибкие, масштабируемые и отказоустойчивые решения. Устройства и логические блоки больше не связаны друг с другом напрямую; они общаются через общего посредника, публикуя события и подписываясь на интересующую информацию.
Приступая к проектированию системы на базе MQTT, инженер должен принять два важнейших архитектурных решения:
Что дальше
Мы рассмотрели основы, но у MQTT есть и более продвинутые функции, которые позволяют решать сложные задачи. В следующем уроке, `COURSE-06-M08-L02`, мы углубимся в следующие темы:
- "Удержанные" сообщения (Retained messages): Как получить последнее известное состояние устройства сразу после подписки на топик.
- Постоянные сессии (Persistent sessions): Как брокер может накапливать сообщения для клиента, который временно находится офлайн.
- Мосты (Bridges): Как настроить связь между двумя разными MQTT-брокерами, например, между локальным брокером на контроллере HI и облачным сервисом.