Авторский проект IT-специалиста Олега Барабанова Персональные публикации на тему IT и не только…

Что из себя представляет протокол JSON-RPC и чем он мне нравится

Дисклеймер для тех кто любит ярые холивары на тему выбора лучшего способа обмена данными с сервисами.

На всяких форумах постоянно любят устраивать холивары на тему REST vs JSON-RPC vs gRPC и т.д. Сразу хочу сказать, что в статье я намеренно стараюсь избегать их полного сравнения, поскольку это бессмысленно. У каждого из них есть свои сильные стороны и в зависимости от задачи, одна технология может быть предпочтительнее другой. По этой же причине я рекомендую расширять свой кругозор и знакомиться с несколькими технологиями, а не пихать везде одно и то же решение в стиле Золотого Молотка.

С протоколом JSON-RPC я впервые ознакомился лет 8 или 10 назад, когда к нашей CMS прикручивал внешний API и тогда я искал какое-либо простое, но стандартизированное решение и при этом не такое многословное как XML-RPC. И по мере поисков оптимальных решений, я познакомился с протоколом JSON-RPC который тогда искренне поразил меня своей простотой.

Что такое JSON-RPC

JSON-RPC - это транспортонезависимый протокол вызова удаленных процедур (RPC - Remote Procedure Call), в котором обмен четко сообщениями происходит в формате JSON, структура которого регламентирована спецификацией. Вся спецификация JSON RPC 2.0 размещена на одной странице и сама по себе небольшая и наглядная.

Обратите внимание на то, что JSON-RPC транспортонезависимый протокол (привет модели OSI) и соответственно вы можете обмениваться сообщениями хоть через HTTP, хоть через голубиную почту.

Давайте теперь рассмотрим формат сообщений, благо он вполне регламентирован стандартом.

Запрос клиента к серверу состоит из объекта с следующими полями:

А вот структура ответа сервера, который передается обратно клиенту (если было указано поле "id"):

Приведем пару примеров:

// Запрос от клиента к серверу
{"jsonrpc": "2.0", "method": "addBook", "params": {author: "М.Ю. Лермонтов", name: "Герой нашего времени"}, "id": "1"}

// Ответ от сервера к клиенту
{"jsonrpc": "2.0", "result": {bookId: 17864, success: true}, "id": "1"}
// Просто отправляем уведомления серверу в одностороннем порядке (т.е. не указываем id и соответственно не получаем ответа)
{"jsonrpc": "2.0", "method": "writeToLog", "params": {user: "Иванов", text: "Пользователь вошел в систему", "datetime": "2025-01-30 00:08:33"}}

{"jsonrpc": "2.0", "method": "writeToLog", "params": {user: "Иванов", text: "Пользователь что-то там сделал в системе", "datetime": "2025-01-30 00:10:45"}}

{"jsonrpc": "2.0", "method": "writeToLog", "params": {user: "Иванов", text: "Система что-то сделала с пользователем", "datetime": "2025-01-30 00:12:50"}}

{"jsonrpc": "2.0", "method": "writeToLog", "params": {user: "Иванов", text: "Пользователь вышел из системы", "datetime": "2025-01-30 00:15:20"}}
// Для примера отправим кривой запрос к серверу (у которого нет указанного метода)
{"jsonrpc": "2.0", "method": "runUnknownMethod", "id": "44"}

// В ответ ожидается ошибка вида:
{"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": "44"}

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

Возможность отправки пакетных запросов (batch-запросы)

Отдельной возможностью JSON-RPC является отправка пакетных запросов. Т.е. вы отправляете не по одному запросу, а целыми пачками.

В таком случае, клиент на сервер отправляет не запрос-объект, а уже массив вышеприведенных объектов. Соответственно и ответ от сервера также должен представлять собой массив объектов-ответов, как на примере ниже:

// Для примера отправим на сервер несколько разнотиповых запросов:
[
  {"jsonrpc": "2.0", "method": "addUser", "params": {name: "Иванов"}, "id": "1"},
  {"jsonrpc": "2.0", "method": "addUser", "params": {name: "Петров"}, "id": "2"},
  {"jsonrpc": "2.0", "method": "addUser", "params": {name: "Сидоров"}, "id": "3"},
  {"jsonrpc": "2.0", "method": "removeUser", "params": {userId: "7785-876"}, "id": "4"},
  {"jsonrpc": "2.0", "method": "writeToLog", "params": {user: "Иванов", text: "Пример безответной записи в лог"},
  {"jsonrpc": "2.0", "method": "runUnknownMethod", "id": "5"}
]

// В ответ клиенту соответственно также придет массив:
[
  {"jsonrpc": "2.0", "result": {userId: "1234-567", success: true}, "id": "1"},
  {"jsonrpc": "2.0", "result": {userId: "2345-678", success: true}, "id": "2"},
  {"jsonrpc": "2.0", "result": {userId: "3456-789", success: true}, "id": "3"},
  {"jsonrpc": "2.0", "result": {success: true}, "id": "4"},
  {"jsonrpc": "2.0", "error": {"code": -32601, "message": "Method not found"}, "id": "5"}
]

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

Что мне нравится в JSON-RPC

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

Естественно у этого протокола есть и определенные неудобства, как например обмен бинарными данными, но это в основном исходит из проблем самого формата JSON. Т.к. протокол транспортонезависимый, то либо придется кодировать бинарные данные в base64 и передавать в параметрах (что увеличит передаваемый объем сообщения и скорость его обработки, но зато универсально), либо воспользоваться помощью стороннего протокола, передавая бинарные данные через него. Нередко так и делают, когда реализуют JSON-RPC поверх HTTP, где и JSON-сообщение и бинарные данные передаются в теле HTTP-запроса в виде multipart/form-data.

Заключение

Как и многие другие протоколы обмена данными с сервисами, JSON-RPC также имеет свои сильные и слабые стороны. Просто на мой взгляд, одной из задач разработчика является адекватный подбор наиболее подходящей технологии (архитектуры, протоколов и пр.) под конкретную задачу и при этом еще желательно не перемудрить и не переусложнить.

Лично я считаю, что JSON-RPC вполне достоин того, чтобы обратить на него внимание. Его изучение не займет много времени и сил, зато возможно в каких-то задачах он вполне сможет оказаться удачным решением.

↑ ↓