Привет всем любителям IoT и железок!
В этой статье я расскажу про, пожалуй, самый популярный протокол передачи данных, используемый в сфере Интернета вещей, — MQTT. А если конкретнее, то про MQTT Version 5.0 (версия, опубликованная 7 марта 2019 года). А если еще конкретнее, — про приятные нововведения версии 5.0 по сравнению с версией 3.1.1.
Кстати, а почему v5.0? Куда делась версия v4.0?
Коренная причина лежит в заголовке пакета CONNECT. Версия протокола или, если быть более точным, уровень версии протокола устанавливается в байте 7.
“The 8-bit unsigned value that represents the revision level of the protocol used by the Client. The value of the Protocol Level field for the version 3.1.1 of the protocol is 4 (0x04).”
Уровень протокола v3.1 определяется как 3, а v3.1.1 — 4. Следовательно, следующий уровень протокола — 5. Чтобы избежать несоответствия, группа спецификации согласилась назвать следующую версию MQTTv5.0:
“The value of the Protocol Version field for version 5.0 of the protocol is 5 (0x05).”
По словам (от 11 декабря 2019 года) Lee Stacey, product evangelist at Thingstream, скоро MQTT будет основным протоколом обмена данными между датчиками.
“В настоящее время устройства IoT используют разные протоколы обмена сообщениями, например: XMPP, AMQP, DDS. Такая фрагментация была препятствием для прогресса в IoT с самого начала.
Протоколы обмена сообщениями — одна из областей, где требуется стандартизация.
Вероятно, лидирующую позицию займет MQTT. Он существует уже более 25 лет, прост, потребляет мало энергии — в сочетании с другими преимуществами это делает его идеальным стандартом для IoT. Дополнительные функции, добавленные в MQTT для ретрансляции данных из сенсорных сетей, позволяют IoT-устройствам использовать для отправки сообщений еще меньше энергии.”
А раз такое дело, то мне была поставлена задача — внедрить поддержку пятой версии MQTT в один из сервисов архитектуры платформы Интернета вещей. Пришлось изучить довольно много страниц протокола, так что спешу поделиться своими наработками и понимаем происходящего.
Прим. — Далее по тексту буду называть topic — топиком, а payload — полезной нагрузкой.
Наиболее важно, что MQTTv5.0 не имеет обратной совместимости (как v3.1.1). Очевидно, что введено слишком много нового, поэтому необходимо пересмотреть существующие реализации.
Согласно спецификации, MQTT v5.0 добавляет значительное количество новых функций в MQTT, сохраняя большую часть ядра на месте.
Основные функциональные цели MQTTv5.0:
- улучшение для крупномасштабных систем в отношении установления связи с тысячами и миллионами устройств;
- улучшение составления отчетов об ошибках (код причины и строка причины);
- формализация общих паттернов взаимодействия, включая определение возможностей взаимодействующих устройств и ответы на запросы;
- добавление механизмов расширяемости, включая пользовательские свойства, формат полезной нагрузки и тип контента;
- увеличение производительности и улучшенная поддержка для небольших клиентов.
Основные новые функции можно разделить, для наглядности, на следующие группы:
- Расширенная аутентификация (Enhanced authentication)
- Истечение сеанса (Session expiration)
- Клиентские и серверные ограничения (Client and Server restrictions/limitations)
- Интервал задержки Will Message (Will Delay Interval)
- Перенаправление сервера (Server Reference or Server Redirect)
2. Особенности публикации сообщений
- Интервал истечения сообщения (Message Expiry Interval)
- Индикатор формата полезной нагрузки и тип содержимого (Payload format indicator & Content type)
- Псевдонимы для топиков (Topic Aliases)
- Ответ на запрос (Response Topic)
- Отсутствие локальных публикаций (Non local publishing)
- Контроль сохраненных сообщений (Retained message control)
- Идентификатор подписки (Subscription identifier)
- Общие подписки (Shared subscriptions)
- Коды и строки причин на все ACK-сообщения (Reason codes on all ACK messages)
- Отключение сервера (Server disconnect)
- Пользовательские свойства (User properties)
Особенности подключения
Расширенная аутентификация (см. п. 4.12 в спецификации)
В MQTTv5.0 теперь доступны дополнительные методы аутентификации, помимо имени пользователя и пароля как в MQTTv3.1.1 (см. свойства Authentication Method и Authentication Data).
Истечение сеанса
В MQTTv3.1.1 и более ранних версиях клиент может управлять тем, как сервер обрабатывает сеанс клиента (сеанс означает подписку клиента и любые сообщения в очереди) с помощью флага «чистый сеанс». Если установлено значение 1, сервер удалит любой существующий сеанс для этого клиента и не сохранит сеанс после отключения. Если установлено значение 0, сервер восстановит любой существующий сеанс для клиента при повторном подключении и сохранит сеанс при отключении клиента.
В MQTTv5.0 этот механизм в основном сохранился, за исключением того, что теперь появился таймер истечения сеанса (см. свойство Session Expiry Interval). Если флаг установлен как false, нужно указать значение окончания сеанса. Если этого не сделать, будет установлено значение 0, что делает соединение таким же, как если бы значение чистого сеанса было установлено как true.
Например, можно установить время истечения сеанса 300 секунд. Это означает, что если клиент отключается и повторно подключается в течение 5 минут со значением чистого сеанса false, QOS > 1, то данные состояния сеанса (например, подписка на топики, сообщения в очереди) сохраняются. Однако, если клиент отключается и повторно подключается по истечении 5 минут с теми же настройками, данные о состоянии сеанса удаляются и клиент должен повторно подписаться на топики, а сообщения, отправленные во время отсоединения клиента, теряются.
Клиентские ограничения
Клиент теперь может указывать серверу следующие ограничения:
- по размеру сообщений для отправки (см. свойство Maximum Packet Size),
- по количеству сообщений QOS 1 и 2 (см. свойство Receive Maximum).
Ограничение размера сообщений
Клиент сообщает серверу максимальный размер сообщения, которое он готов получить, и любые сообщения, превышающие это значение, отбрасываются сервером. Используется маломощными клиентами, которым не хватает памяти для хранения сообщений.
Ограничение количества сообщений
Клиент сообщает серверу об ограничении количества сообщений QOS 1 и 2, которые могут быть отправлены одновременно. Также используется маломощными клиентами, поскольку сообщения QOS 1 и 2 означают, что клиент должен отправить дополнительные сообщения подтверждения.
Серверные ограничения
Предоставление определенных функций может привести к нагрузке на серверные ресурсы, поэтому сервер может их не поддерживать. Теперь сервер может указать это, используя поле свойств.
Например, в сообщении CONNACK сервер может указать максимальное значение псевдонима топика (см. свойство Topic Alias Maximum) и время истечения сеанса (см. свойство Session Expiry Interval).
Интервал задержки Will Message
Используется для указания временной задержки (см. свойство Will Delay Interval) при отправке Will Message, предотвращая отправку сообщения при коротком прерывании соединения.
Перенаправление сервера (см. п. 4.11 в спецификации)
Это очень интересная функция, поскольку она позволяет серверу перенаправлять клиента к другому брокеру/серверу (см. свойство Server Reference).
Особенности публикации сообщений
Интервал истечения сообщения
Когда сообщение публикуется с QOS 1 или 2 и клиент-получатель имеет:
- чистый сеанс false,
- подписку с QOS 1 или 2,
брокер будет хранить опубликованные сообщения для клиента, если он в данный момент отключен.
Тем не менее можно установить период, в течение которого сервер будет сохранять это сообщение. Например, указать значение свойства Message Expiry Interval равным 20. При такой настройке, если клиент-получатель не подключится в течение 20 секунд, сообщение удалится.
Индикатор формата полезной нагрузки и тип содержимого
Индикатор формата полезной нагрузки устанавливается при публикации сообщения (см. свойство Payload Format Indicator) как один из двух вариантов:
- бинарный — то же самое, что в v3.1,
- строка UTF-8 — новая возможность v5.
Функция полезна для получателя, чтобы узнать, нужно ли ему декодировать полезную нагрузку сообщения.
Также при публикации сообщений можно указать тип содержимого в стиле MIME (см. свойство Content Type).
Псевдонимы для топиков
Псевдонимы являются механизмом уменьшения размера публикуемых пакетов за счет уменьшения размера топика.
Созданный псевдоним топика можно использовать вместо самого топика. Если у топика homes/house1/upstairs/light1 есть псевдоним 1 (см. свойство Topic Alias), то это число можно использовать вместо строки топика при публикации сообщений.
Прежде чем использовать псевдонимы топиков, клиент должен указать максимальное количество псевдонимов (см. свойство Topic Alias Maximum) для сервера. По умолчанию, если это не установлено, псевдонимы топиков клиентом не используются.
Ответ на запрос
В веб-приложениях клиент(браузер) делает запрос, а сервер отвечает. В то же время MQTT использует шаблон публикации и подписки, где нет прямой связи между отправляющим и получающим клиентом.
Использование топика для ответа (см. свойство Response Topic) в сообщении публикации позволяет реализовать шаблон запроса/ответа, распространенный в веб-приложениях. Таким образом создается виртуальное соединение между клиентами, так что один из клиентов может функционировать как традиционный сервер, хотя пакеты всё так же проходят через брокера.
Особенности подписки
Отсутствие локальных публикаций
В MQTTv3.1.1, если вы подписались на топик, по которому публикуете сообщения, вы получите все сообщения, которые вы публикуете. Используя опцию отсутствия локальных публикаций, можно избежать того, чтобы брокер отправлял вам сообщения, которые вы сами же и опубликовали.
Контроль сохраненных сообщений
Сохраненные сообщения по-прежнему работают, как в версии 3.1.1, но теперь добавлены параметры подписки для управления тем, что получает клиент:
- 0 — отправить сохраненные сообщения, когда клиент подписывается. То же, что и в MQTT 3.1.1,
- 1 — отправить сохраненные сообщения, когда клиент подписывается, если подписка в настоящее время не существует.
- 2 — не отправлять сохраненные сообщения, когда клиент подписывается.
Идентификатор подписки
При подписке можно указывать идентификатор (см. свойство Subscription Identifier), соответствующий этой подписке. Тогда при получении сообщения клиент имеет возможность определить, какая подписка или подписки привели к доставке сообщения.
Общие подписки (см. п. 4.8.2 в спецификации)
Идея заключается в том, чтобы обеспечить балансировку нагрузки клиента. При обычных подписках (не общих подписках), если, например, 4 клиента подписываются на один и тот же топик и клиент публикует сообщение с таким топиком, то сообщение отправляется всем 4 подписавшимся клиентам.
При общей подписке сообщение будет отправлено только одному из подписавшихся клиентов.
Топик для такой подписки должен начинаться с ключевого слова $share.
Формат фильтра топиков следующий:
$share/{ShareName}/{filter}
$share — служебное слово, символьная строка, которая помечает фильтр топиков как фильтр топиков общей подписки
{ShareName} — название подписки, символьная строка, которая не должна включать "/", "+" или "#"
{filter} — то же самое, что топик в обычных подписках
Пример:
4 клиента подписываются на $share/group1/my/topic, 3 клиента подписываются на $share/group2/my/topic, и 3 клиента подписываются по обычной подписке (2 — my/topic и 1 — по wildcard подписке my/#).
Затем, если клиент публикует сообщение для топика my/topic:
- 1 сообщение отправлено одному из клиентов, подписавшихся на $share/group1/my/topic
- 1 сообщение отправлено одному из клиентов, подписавшихся на $share/group2/my/topic
- 3 сообщения отправлено клиентам с обычной подпиской
Таким образом, в общей сложности 5 сообщений отправляются клиентам.
Общие особенности
Коды и строки причин на все ACK-сообщения
В MQTTv3.1.1 было очень мало указаний от сервера относительно того, что пошло не так на разных этапах:
- установление связи,
- публикация сообщения,
- подписка на топики.
Теперь все пакеты подтверждения (кроме PINGRESP) содержат код причины, доступно 128 кодов. Приложению становится намного проще понять, что же произошло, и предпринять соответствующие действия.
Например, публикация ACK с кодом причины 16 означает, что нет подходящих подписчиков. Поэтому в MQTTv5 можно определить, в какой степени используется тот или иной топик.
Следует помнить, что коды причины являются необязательными, сервер/брокер все еще может решить просто отключить клиентов как в MQTTv3.1.1, например, по соображениям безопасности.
В дополнение к коду причины строка причины может быть связана с ответом, чтобы предоставить более удобочитаемое сообщение.
Отключение сервера
В MQQTv3.1.1 только клиент может отправить сообщение об отключении. Если сервер столкнулся с проблемами, он просто прервал бы сеанс TCP/IP. В MQTTv5 сервер может отправить клиенту сообщение о разъединении вместе с кодом причины (ошибка протокола, неавторизованное подключение и т.д.).
Примеры кодов причины из спецификации (см. п. 3.14.2.1 в спецификации):
Пользовательские свойства
Добавлены во все типы пакетов. Они определяются пользователем и представляют собой набор пар ключ-значение (см. свойство User Property). Полезны для отправки информации за рамками полезной нагрузки, в то время как в 3.1.1 любая информация отправлялась как часть полезной нагрузки.
Пользовательские свойства, как и все другие свойства, пересылаются брокером от отправителя получателя без изменений и в том же порядке. Может быть добавлено неограниченное количество пользовательских свойств.
Заключение
Пока на этом всё. В следующей статье я проведу подробный разбор всех свойств, добавленных в спецификацию.
Автор: Кристина Голдинова