Бөлісу құралы:


Протокол хранилища состояний брокера MQTT

Внимание

Предварительная версия операций Интернета вещей Azure, включенная Azure Arc в настоящее время в предварительной версии. Не следует использовать это программное обеспечение предварительной версии в рабочих средах.

Вам потребуется развернуть новую установку Операций Интернета вещей Azure, когда общедоступная версия станет доступной. Вы не сможете обновить предварительную установку.

Юридические условия, применимые к функциям Azure, которые находятся в бета-версии, предварительной версии или в противном случае еще не выпущены в общедоступную версию, см . в дополнительных условиях использования для предварительных версий Microsoft Azure.

Хранилище состояний MQ — это распределенная система хранения в кластере операций Интернета вещей Azure. Хранилище состояний предлагает те же гарантии высокой доступности, что и сообщения MQTT в брокере MQTT. В соответствии с рекомендациями по протоколу MQTT5/RPC клиенты должны использовать MQTT5 для взаимодействия с хранилищем состояний MQ. В этой статье приводятся рекомендации по протоколу для разработчиков, которым необходимо реализовать собственные клиенты хранилища состояний брокера MQTT.

Общие сведения о протоколе хранилища состояний

Хранилище состояний MQ поддерживает следующие команды:

  • SET<keyName><keyValue><setOptions>
  • GET<keyName>
  • DEL<keyName>
  • VDEL<keyName><keyValue> ## Удаляет заданное <имя> ключа, если и только если его значение является <keyValue>

Протокол использует следующую модель ответа на запрос:

  • Запрос. Клиенты публикуют запрос в четко определенный системный раздел хранилища состояний. Для публикации запроса клиенты используют необходимые свойства и полезные данные, описанные в следующих разделах.
  • Ответ. Хранилище состояний асинхронно обрабатывает запрос и отвечает на раздел ответа, который клиент первоначально предоставил.

На следующей схеме показано базовое представление запроса и ответа:

Схема базового процесса запроса и ответа хранилища состояний.

Системный раздел хранилища состояний, QoS и обязательные свойства MQTT5

Чтобы взаимодействовать с хранилищем состояний, клиенты должны соответствовать следующим требованиям:

  • Используйте MQTT5. Дополнительные сведения см. в спецификации MQTT 5.
  • Используйте QoS 1 (уровень обслуживания 1). QoS 1 описано в спецификации MQTT 5.
  • У вас есть часы, которые находится в течение одной минуты от часов брокера MQTT.

Чтобы взаимодействовать с хранилищем состояний, клиенты должны PUBLISH запрашивать системный раздел statestore/v1/FA9AE35F-2F64-47CD-9BFF-08E2B32A0FE8/command/invoke. Так как хранилище состояний является частью Операций Интернета вещей Azure, оно неявно SUBSCRIBE относится к этой теме при запуске.

Чтобы создать запрос, требуются следующие свойства MQTT5. Если эти свойства отсутствуют или запрос не имеет типа QoS 1, запрос завершается ошибкой.

  • Раздел ответа. Хранилище состояний отвечает на первоначальный запрос, используя это значение. Рекомендуется форматировать раздел ответа как clients/{clientId}/services/statestore/_any_/command/invoke/response. Установка раздела ответа как statestore/v1/FA9AE35F-2F64-47CD-9BFF-08E2B32A0FE8/command/invoke или как того, с который начинается, clients/statestore/v1/FA9AE35F-2F64-47CD-9BFF-08E2B32A0FE8 не разрешено в запросе хранилища состояний. Хранилище состояний отключает клиенты MQTT, использующие недопустимый раздел ответа.
  • Данные корреляции. Когда хранилище состояний отправляет ответ, оно включает данные корреляции начального запроса.

На следующей схеме показано расширенное представление запроса и ответа:

Схема развернутого процесса запроса и ответа хранилища состояний.

Поддерживаемые команды

Команды SETGETи DEL поведение, как ожидалось.

Значения, полученные SET наборами команд и GET извлекаемой командой, являются произвольными двоичными данными. Размер значений ограничен максимальным размером полезных данных MQTT и ограничениями ресурсов MQQ и клиента.

SET options

Команда SET предоставляет более необязательные флаги за рамки основных keyValue и keyName:

  • NX. Позволяет задать ключ только в том случае, если он еще не существует.
  • NEX <value>. Позволяет задать ключ только в том случае, если ключ не существует или значение ключа уже задано значением<>. Обычно NEX флаг используется для клиента, обновляющего срок действия ключа (PX).
  • PX. Как долго ключ должен сохраняться до истечения срока его действия в миллисекундах.

VDEL options

Команда VDEL — это особый случай DEL команды. DEL безусловно удаляет указанный keyName. VDEL требует другого аргумента, вызываемого keyValue. VDEL удаляет только указанный keyName параметр, если он имеет то же самое keyValue.

Формат полезных данных

Формат полезных данных хранилища PUBLISH состояний вдохновлен RESP3, который является базовым протоколом, используемым Redis. RESP3 кодирует как команду, так и параметры, такие как SET keyName и keyValue.GET

Учет регистра

Клиент должен отправлять как команды, так и параметры в верхнем регистре.

Формат запроса

Запросы форматируются, как показано в следующем примере. После RESP3 отображается * количество элементов в массиве. Символ $ — это число символов в следующей строке, за исключением конечного CRLF.

Поддерживаемые команды в формате RESP3: GET, SETDELи VDEL.

*{NUMBER-OF-ARGUMENTS}<CR><LF>
${LENGTH-OF-NEXT-LINE}<CR><LF>
{COMMAND-NAME}<CR><LF>
${LENGTH-OF-NEXT-LINE}<CR><LF> // This is always the keyName with the current supported verbs.
{KEY-NAME}<CR><LF>
// Next lines included only if command has additional arguments
${LENGTH-OF-NEXT-LINE}<CR><LF> // This is always the keyValue for set
{KEY-VALUE}<CR><LF>

В следующем примере выходных данных показаны полезные данные хранилища состояний RESP3:

*3<CR><LF>$3<CR><LF>set<CR><LF>$7<CR><LF>SETKEY2<CR><LF>$6<CR><LF>VALUE5<CR><LF>
*2<CR><LF>$3<CR><LF>get<CR><LF>$7<CR><LF>SETKEY2<CR><LF>
*2<CR><LF>$3<CR><LF>del<CR><LF>$7<CR><LF>SETKEY2<CR><LF>
*3<CR><LF>$4<CR><LF>vdel<CR><LF>$7<CR><LF>SETKEY2<CR><LF>$3<CR><LF>ABC<CR><LF>

Примечание.

Обратите внимание, что SET требуются дополнительные свойства MQTT5, как описано в разделе "Управление версиями" и гибридные логические часы.

Формат ответа

Когда хранилище состояний обнаруживает недопустимую полезные данные RESP3, он по-прежнему возвращает ответ на запрос.Response Topic Примеры недопустимых полезных данных включают недопустимую команду, недопустимую команду RESP3 или целый поток переполнения. Недопустимая полезные данные начинается со строки -ERR и содержит дополнительные сведения.

Примечание.

DELVDEL Значение или GETзапрос на несуществующий ключ не считается ошибкой.

Если клиент отправляет недопустимую полезные данные, хранилище состояний отправляет полезные данные, как показано в следующем примере:

-ERR syntax error

SET ответ

При успешном выполнении запроса хранилище состояний SET возвращает следующие полезные данные:

+OK<CR><LF>

Если запрос SET завершается ошибкой, так как проверка условия, указанная в параметрах набора NX или NEX, то это означает, что ключ не может быть задан, хранилище состояний возвращает следующую полезные данные:

-1<CR><LF>

GET ответ

GET При выполнении запроса на несуществующий ключ хранилище состояний возвращает следующие полезные данные:

$-1<CR><LF>

При обнаружении ключа хранилище состояний возвращает значение в следующем формате:

${NumberOfBytes}<CR><LF>
{KEY-VALUE}

Выходные данные хранилища состояний, возвращающего значение 1234 , выглядят следующим образом:

$4<CR><LF>1234<CR><LF>

DEL и VDEL ответ

Хранилище состояний возвращает количество значений, которые он удаляет при запросе на удаление. В настоящее время хранилище состояний может удалять только одно значение за раз.

:{NumberOfDeletes}<CR><LF> // Will be 1 on successful delete or 0 if the keyName is not present

Ниже приведен пример успешной DEL команды:

:1<CR><LF>

Если запрос VDEL завершается ошибкой, так как указанное значение не соответствует значению, связанному с ключом, хранилище состояний возвращает следующие полезные данные:

-1<CR><LF>

-ERR Ответы

Ниже приведен текущий список строк ошибок. Клиентское приложение должно обрабатывать неизвестные строки ошибок для поддержки обновлений в хранилище состояний.

Строка ошибки, возвращенная из хранилища состояний Описание
Метка времени запроса слишком далеко в будущем; убедитесь, что системные часы клиента и брокера синхронизируются Непредвиденная метка времени запроса, вызванная хранилищем состояний и часами клиента, не синхронизируются.
Для этого запроса требуется маркер ограждения Ошибка возникает, если ключ помечается маркером ограждения, но клиент не указывает маркер ограждения.
Метка времени маркера ограждения запроса слишком далеко в будущем; убедитесь, что системные часы клиента и брокера синхронизируются Непредвиденная метка времени маркера ограждения, вызванная хранилищем состояний и часами клиента, не синхронизируются.
Маркер ограждения запроса является более низкой версией, что маркер ограждения, защищающий ресурс. Неправильная версия маркера ограждения запроса. Дополнительные сведения см. в разделе [Управление версиями и гибридные логические часы]. (#versioning-и-гибридные-логические часы)
Превышена квота Хранилище состояний имеет квоту на количество ключей, которые он может хранить, в зависимости от профиля памяти брокера MQTT, указанного.
синтаксическая ошибка Полезные данные, отправленные, не соответствуют определению хранилища состояний.
не авторизовано Ошибка авторизации
Неизвестная команда Команда не распознана.
неправильное число аргументов Неправильное число ожидаемых аргументов.
отсутствует метка времени Когда клиенты выполняют НАБОР, они должны задать свойство пользователя MQTT5 __ts как HLC, представляющее метку времени.
неправильно сформированная метка времени Метка времени в __ts или маркере ограждения не является законным.
Длина ключа равна нулю Ключи не могут быть нулевой длиной в хранилище состояний.

Управление версиями и гибридные логические часы

В этом разделе описывается, как хранилище состояний обрабатывает управление версиями.

Версии в виде гибридных логических часов

Хранилище состояний поддерживает версию для каждого значения, которое он сохраняет. Хранилище состояний может использовать монотонный счетчик для поддержания версий. Вместо этого хранилище состояний использует гибридные логические часы (HLC) для представления версий. Дополнительные сведения см. в статьях о первоначальном проектировании HLCs и намерении, лежащих в основе HLCs.

Хранилище состояний использует следующий формат для определения HLCs:

{wallClock}:{counter}:{node-Id}

Число wallClock миллисекунда с эпохи Unix. counter и node-Id работайте в качестве HLCs в целом.

При выполнении SETклиентами необходимо задать свойство __ts пользователя MQTT5 как HLC, представляющее метку времени на основе текущих часов клиента. Хранилище состояний возвращает версию значения в своем ответном сообщении. Ответ также указывается как HLC, а также использует __ts свойство пользователя MQTT5. Возвращаемый HLC всегда больше HLC начального запроса.

Пример установки и получения версии значения

В этом разделе показан пример настройки и получения версии для значения.

Наборы keyName=valueклиентов. Часы клиента — 3 октября 11:07:05 GMT. Значение часов — 1696374425000 миллисекунда с эпохи Unix. Предположим, что системные часы хранилища состояний идентичны часам клиентской системы. Клиент выполняет SET команду, как описано ранее.

На следующей схеме показана SET команда:

Схема команды хранилища состояний, чтобы задать версию значения.

Свойство __ts (timestamp) в начальном наборе содержит как 1696374425000 клиентские стенные часы, счетчик как 0и идентификатор узла CLIENT. В ответе __ts свойство, возвращаемое хранилищем состояний, содержит wallClockсчетчик, который увеличивается по одному, а идентификатор узла — как StateStore. Хранилище состояний может вернуть более высокое wallClock значение, если его часы были впереди, исходя из способа работы обновлений HLC.

Эта версия также возвращается при успешном GETвыполнении VDEL и DELзапросах. В этих запросах клиент не указывает __ts.

На следующей схеме показана GET команда:

Схема хранилища состояний, получающего версию значения.

Примечание.

Метка __ts времени, возвращаемая хранилищем состояний, совпадает с тем, что она возвращается при первоначальном SET запросе.

Если указанный SETключ позже обновляется новым, процесс аналогичен. Клиент должен задать свой запрос __ts на основе текущих часов. Хранилище состояний обновляет версию значения и возвращает __tsзначение, следуя правилам обновления HLC.

Разница в показаниях часов

Хранилище состояний отклоняет __ts (а также __ft) больше минуты впереди местных часов хранилища состояний.

Хранилище состояний принимает значение __ts , которое находится за локальными часами хранилища состояний. Как указано в алгоритме HLC, хранилище состояний задает версию ключа в локальные часы, так как это больше.

Блокировка и ограждение маркеров

В этом разделе описывается назначение и использование маркеров блокировки и ограждения.

Общие сведения

Предположим, что существует два или более клиентов MQTT с помощью хранилища состояний. Оба клиента хотят записать в заданный ключ. Клиенты хранилища состояний нуждаются в механизме блокировки ключа таким образом, что только один клиент за раз может изменить заданный ключ.

Пример этого сценария возникает в активных и резервных системах. Может быть два клиента, которые выполняют одну и ту же операцию, и операция может включать один и тот же набор ключей хранилища состояний. В данный момент один из клиентов активен, а другой стоит немедленно взять на себя, если активная система зависает или сбой. В идеале только один клиент должен записывать данные в хранилище состояний в определенное время. Однако в распределенных системах возможно, что оба клиента могут вести себя так, как будто они активны, и они могут одновременно пытаться записать в одни и те же ключи. Этот сценарий создает условие гонки.

Хранилище состояний предоставляет механизмы предотвращения этого состояния гонки с помощью маркеров ограждения. Дополнительные сведения о маркерах ограждения и классе условий гонки, которые они предназначены для защиты от, см. в этой статье.

Получение маркера ограждения

В этом примере предполагается, что у нас есть следующие элементы:

  • Client1 и Client2. Эти клиенты являются клиентами хранилища состояний, которые действуют как активная и резервная пара.
  • LockName. Имя ключа в хранилище состояний, которое выступает в качестве блокировки.
  • ProtectedKey. Ключ, который необходимо защитить от нескольких писателей.

Клиенты пытаются получить блокировку в качестве первого шага. Они получают блокировку, делая SET LockName {CLIENT-NAME} NEX PX {TIMEOUT-IN-MILLISECONDS}. Отзыв из параметра задания , что флаг означает, что NEX флаг успешно выполняется только в том случае, SET если выполняется одно из следующих условий:

  • Ключ был пустым
  • Значение ключа уже задано <как значение> и PX указывает время ожидания в миллисекундах.

Предположим, что сначала Client1 выполняется запрос SET LockName Client1 NEX PX 10000. Этот запрос предоставляет ему право собственности LockName на 10 000 миллисекундах. Если Client2 выполняется попытка в SET LockName Client2 NEX ... некоторое время Client1 владеет блокировкой, NEX флаг означает, что запрос завершается ошибкой Client2 . Client1 необходимо продлить эту блокировку, отправив ту же SET команду, которая использовалась для получения блокировки, если Client1 хотите продолжить владение.

Примечание.

A SET NX концептуально эквивалентен AcquireLock().

Использование маркеров ограждения для запросов SET

При Client1 успешном SET выполнении операции ("AquireLock") хранилище состояний возвращает версию LockName в качестве гибридной логической часы (HLC) LockNameв свойстве __tsпользователя MQTT5.

При выполнении SET запроса клиент может дополнительно включить свойство __ft пользователя MQTT5 для представления маркера ограждения. Он __ft представлен как HLC. Маркер ограждения, связанный с заданной парой "ключ-значение", обеспечивает проверку владения блокировкой. Маркер ограждения может поступать из любого места. Для этого сценария она должна быть получена из версии LockName.

На следующей SET схеме показан процесс Client1 выполнения запроса поLockName:

Схема клиента, выполняющего запрос набора в свойстве имени блокировки.

Client1 Затем использует __ts свойство (Property=1696374425000:1:StateStore) неизмененные в качестве основы __ft свойства в запросе для измененияProtectedKey. Как и все SET запросы, клиент должен задать __ts свойство ProtectedKey.

На следующей SET схеме показан процесс Client1 выполнения запроса поProtectedKey:

Схема клиента, выполняющего запрос набора для свойства защищенного ключа.

Если запрос выполнен успешно, с этого момента ProtectedKey требуется маркер ограждения, равный или больше указанного в запросе SET .

Алгоритм маркера ограждения

Хранилище состояний принимает любое HLC для __ts пары "ключ-значение", если значение находится в пределах максимального количества часов. Однако то же самое не верно для маркеров ограждения.

Алгоритм хранилища состояний для маркеров ограждения выглядит следующим образом:

  • Если пара "ключ-значение" не связана с ним маркером ограждения и SET набором __ftзапросов, хранилище состояний сохраняет связанное __ft с парой "ключ-значение".
  • Если пара "ключ-значение" имеет маркер ограждения, связанный с ним:
    • SET Если запрос не указан__ft, отклоните запрос.
    • SET Если запрос указал __ft более старое значение HLC, чем маркер ограждения, связанный с парой "ключ-значение", отклоните запрос.
    • SET Если запрос указал __ft значение, равное или более новое значение HLC, чем маркер ограждения, связанный с парой "ключ-значение", примите запрос. Хранилище состояний обновляет маркер ограждения пары "ключ-значение", чтобы он был одним из наборов в запросе, если он более новый.

После того как ключ помечается маркером ограждения, для успешного выполнения запроса и DEL VDEL запросов также требуется __ft включить свойство. Алгоритм идентичен предыдущему, за исключением того, что маркер ограждения не хранится, так как ключ удаляется.

Поведение клиента

Эти механизмы блокировки полагаются на то, что клиенты хорошо ведут себя. В предыдущем примере неправильное поведение Client2 не может принадлежать LockName и по-прежнему успешно выполнять SET ProtectedKey его, выбрав маркер ограждения, который является более новым, чем ProtectedKey токен. Хранилище состояний не знает, что LockName и ProtectedKey имеет какие-либо отношения. В результате хранилище состояний не выполняет проверку, которая Client2 фактически владеет значением.

Клиенты, способные писать ключи, для которых они фактически не принадлежат блокировке, нежелательно. Вы можете защититься от таких ошибок клиента, правильно реализуя клиенты и используя проверку подлинности, чтобы ограничить доступ к ключам только доверенным клиентам.

Notifications

Клиенты могут регистрироваться в хранилище состояний для получения уведомлений об изменении ключей. Рассмотрим сценарий, в котором термостат использует ключ {thermostatName}\setPointхранилища состояний. Другие клиенты хранилища состояний могут изменить значение этого ключа, чтобы изменить setPoint термостата. Вместо опроса изменений термостат может регистрироваться в хранилище состояний для получения сообщений при {thermostatName}\setPoint изменении.

Сообщения запроса KEYNOTIFY

Клиенты хранилища состояний запрашивают хранилище состояний для отслеживания изменений keyName , отправляя KEYNOTIFY сообщение. Как и все запросы хранилища состояний, клиенты публикуют сообщение QoS1 с этим сообщением через MQTT5 в системном разделе statestore/v1/FA9AE35F-2F64-47CD-9BFF-08E2B32A0FE8/command/invokeхранилища состояний.

Полезные данные запроса имеют следующую форму:

KEYNOTIFY<CR><LF>
{keyName}<CR><LF>
{optionalFields}<CR><LF>

Где:

  • KEYNOTIFY — это строковый литерал, указывающий команду.
  • {keyName} — это ключевое имя для прослушивания уведомлений. Подстановочные знаки в настоящее время не поддерживаются.
  • {optionalFields} Поддерживаемые в настоящее время необязательные значения полей:
    • {STOP} Если есть существующее уведомление с таким же keyName запросом, clientId хранилище состояний удаляет его.

В следующем примере выходных данных показан KEYNOTIFY запрос на мониторинг ключа SOMEKEY:

*2<CR><LF>
$9<CR><LF>
KEYNOTIFY<CR><LF>
$7<CR><LF>
SOMEKEY<CR><LF>

Ответное сообщение KEYNOTIFY

Как и все запросы RPC хранилища состояний, хранилище состояний возвращает ответ на Response Topic его и использует Correlation Data свойства, указанные в исходном запросе. Для KEYNOTIFYуспешного ответа указывается, что хранилище состояний обрабатывает запрос. После успешного обработки запроса хранилище состояний отслеживает ключ для текущего клиента или останавливает мониторинг.

При успешном выполнении ответ хранилища состояний совпадает с успешным SET.

+OK<CR><LF>

Если клиент отправляет KEYNOTIFY SOMEKEY STOP запрос, но хранилище состояний не отслеживает этот ключ, ответ хранилища состояний совпадает с попыткой удалить ключ, который не существует.

:0<CR><LF>

Любой другой сбой следует общему шаблону отчетов об ошибках хранилища состояний:

-ERR: <DESCRIPTION OF ERROR><CR><LF>

Разделы и жизненный цикл уведомлений KEYNOTIFY

keyName При изменении или удалении отслеживаемого KEYNOTIFY хранилища состояний отправляется клиенту уведомление. Тема определяется соглашением. Клиент не указывает тему во время KEYNOTIFY процесса.

Раздел определен в следующем примере. Это clientId шестнадцатеричное представление идентификатора клиента MQTT клиента, инициирующего KEYNOTIFY запрос, и keyName является шестнадцатеричным представлением измененного ключа. Хранилище состояний следует правилам кодировки Base 16 RFC 4648 — Base16, Base32 и Base64 Data Encodings для этой кодировки .

clients/statestore/v1/FA9AE35F-2F64-47CD-9BFF-08E2B32A0FE8/{clientId}/command/notify/{keyName}

Например, MQ публикует NOTIFY сообщение, отправленное client-id1 с измененным именем SOMEKEY ключа в раздел:

clients/statestore/v1/FA9AE35F-2F64-47CD-9BFF-08E2B32A0FE8/636C69656E742D696431/command/notify/534F4D454B4559`

Клиент, использующий уведомления, должен SUBSCRIBE быть получен в этом разделе и дождитесь SUBACK получения , прежде чем отправлять все KEYNOTIFY запросы, чтобы сообщения не были потеряны.

Если клиент отключается, он должен повторно подписаться на KEYNOTIFY раздел уведомлений и повторно отправить KEYNOTIFY команду для любых ключей, которые необходимо продолжить мониторинг. В отличие от подписок MQTT, которые могут быть сохранены в неклианном сеансе, хранилище состояний внутренне удаляет все KEYNOTIFY сообщения при отключении определенного клиента.

Формат сообщения уведомления KEYNOTIFY

При изменении ключа, отслеживаемого с помощью KEYNOTIFY , хранилище состояний будет PUBLISH отправлено сообщение в раздел уведомлений после формата для клиентов хранилища состояний, зарегистрированных для изменения.

NOTIFY<CR><LF>
{operation}<CR><LF>
{optionalFields}<CR><LF>

Следующие сведения включаются в сообщение:

  • NOTIFY — строковый литерал, включенный в качестве первого аргумента в полезные данные, указывающий на уведомление.
  • {operation} — это событие, которое произошло. В настоящее время эти операции:
    • SET значение было изменено. Эта операция может происходить только в результате SET команды из клиента хранилища состояний.
    • DEL Значение было удалено. Эта операция может произойти из-за DEL или VDEL команды из клиента хранилища состояний.
  • optionalFields
    • VALUE и {MODIFIED-VALUE}. VALUE — строковый литерал, указывающий, что следующее поле содержит значение, {MODIFIED-VALUE}на которое был изменен ключ. Это значение отправляется только в ответ на изменения ключей из-за SETизменения.

В следующем примере выходных данных показано сообщение уведомления, отправленное при изменении ключа SOMEKEY в значение abc, с VALUE включенным, так как первоначальный запрос указал GET параметр:

*4<CR><LF>
$6<CR><LF>
NOTIFY<CR><LF>
$3<CR><LF>
SET<CR><LF>
$5<CR><LF>
VALUE<CR><LF>
$3<CR><LF>
abc<CR><LF>