Skip to content

MQTT RPC protocol description for Wiren Board

License

Notifications You must be signed in to change notification settings

wirenboard/mqtt-rpc

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

25 Commits
 
 
 
 
 
 

Repository files navigation

MQTT-RPC

Репозиторий содержит описание протокола MQTT-RPC.

Эталонная реализация на Python доступна тут: python-mqtt-rpc.

MQTT-RPC Описание протокола

MQTT-RPC предполагает удалённый вызов клиентом процедур сервиса. Коммуникация осуществляется через MQTT. Формируются топик для запросов и топик для ответов. В качестве значений передаются JSON объекты.

Для клиентов сервисов топик для отправки запросов выглядит как:

/rpc/v1/<driver>/<service>/<method>/<client_id>

Где:

  • /rpc/v1 - общий префикс для всех сервисов RPC версии 1,
  • driver - имя драйвера, предоставляющего сервисы,
  • service - имя сервиса. Соответствует, например, имени класса с методами в коде сервера,
  • method - имя метода, предоставленного сервисом,
  • client_id - идентификатор клиента (можно использовать mqtt client id, если он уникальный, или просто сгенерить что-то рандомное, например, UUID v4).

Например:

/rpc/v1/wb_logs/logs/List/3b131342-9809-4bf0-a036-cbcebd5f42e5

Сервер отправляет ответ на топик, по которому пришёл запрос, с добавлением в конце /reply. Для того чтобы получить ответ от сервиса на свой RPC-зпарос, клиент подписывается на:

/rpc/v1/<driver>/<service>/<method>/<client_id>/reply

client_id здесь - собственный ID клиента, совпадающий с client_id в RPC-запросе.

Например:

/rpc/v1/wb_logs/logs/List/3b131342-9809-4bf0-a036-cbcebd5f42e5/reply

Для того, чтобы рассмотреть все доступные RPC-сервисы и их методы в системе можно запустить команду в терминале контроллера:

mosquitto_sub -t "/rpc/v1/+/+/+" -v

Клиент отправляет посылку в виде JSON объекта. Структура объекта представлена в документации к сервису. Например:

{
  "id": "1234",
  "params": {"A": 1, "B": 2}
}

Здесь:

  • id - идентификатор транзакции. Для простоты реализации сейчас это должна быть строка, являющаяся десятичным представлением 64-битного беззнакового значения. Спецификация JSON-RPC разрешает использование любой строки, но вышеназванное ограничение немного упрощает Go-реализацию, т.к. стандартный пакет net/rpc использует 64-битные идентификаторы транзакций. Использование числовых значений вместо строк - не очень, т.к. JSON'овый Number - это double, и все значения uint64 он вместить не способен. Зачем вообще нужен идентификатор транзакции: допустим, делается несколько запросов GetValue(name) с разным значением параметра name. Запросы обрабатываются асинхронно и ответы могут придти в разном порядке. Возвращается просто число. Как клиенту отличить возвращаемые значения для разных name? Добавлять входные параметры в ответ было бы менее практичным и несовместимым с неидемпотентными операциями. Если использовать идентификатор транзакции в топике, то это приведёт к необходимости subscribe + unsubscribe на каждом RPC-запросе. Subscribe и unsubscribe в MQTT реализованы по аналогии с QoS1 - с подтверждением: SUBSCRIBE - SUBACK, UNSUBSCRIBE-UNSUBACK, так что избыточного MQTT-трафика выйдет порядочно.

  • params - параметры - JSON-объект или массив значений. Go-реализация использует только представление в виде объекта.

Ответ в случае успеха выглядит как:

{ "id": "1234", "result": 42, "error": null }
  • id должен соответствовать идентификатору запроса.
  • result может быть любым JSON-значением (числом, строкой, объектом, массивом...)

В случае ошибки ответ имеет структуру:

{ "id": "1234", "error": { "message": "divide by zero", "code": -1, "data": "ErrorType"} }

или

{ "id": "1234", "error": { "message": "divide by zero", "code": -1} }
  • id должен соответствовать идентификатору запроса.
  • error должен содержать сообщение об ошибке.

Во всех случаях используется строгий стандартный JSON, т.е. комментарии не поддерживаются и все ключи объектов должны быть в кавычках. Отдельно стоит обратить внимание на то, что значения Inf и NaN в JSON не кодируются.