Спецификация API BUS шина

Техническая спецификация работы API. Данная документация определяет принципы, по которым работает API протокол. Эта спецификация общая для Облачных Операционных Систем и для Облачных Интеграторов.

Определения

JWT – JSON Web Token это открытый стандарт (RFC 7519) для создания токенов доступа, основанный на JSON формате. Как правило, используется для передачи данных авторизации в клиент-серверных приложениях.

Frontend – то, что выполняется в браузере клиента, отвечает за динамический HTML.

Backend – обеспечивает обработку API запросов от Frontend'a и выдачи ответа на запрос, обычно работает на стороне сервера, либо в фоне веб приложения.

Bus – универсальная шина обмена данными и событиями между разными узлами системы

BusProvider – транспорт шины, который обеспечивает обмен сообщениями

SystemBus – внутренняя шина системы между ее элементами или микросервисами.

ExternalBus – внешняя шина, взаимодействия между Backend и Frontend.

Работа шины

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

Каждое приложение должно обладать уникальным app_name. По этому ID будут отправляться сообщения в это приложение.

У каждого приложения есть виртуальное пространство - space_id. Пространство - это логический уровень интеграции данных. У одинакового space_id данные в разных приложениях связаны между собой и представляют собой единое пространство данных. В этом пространстве определяются роли и права доступа пользователей.

Внешний gateway может проверить авторизацию или в какой конкретно экземпляр app_name нужно отправить запрос. Например, существует тестовый экземпляр для отладки. Для него создается отдельный тестовый space_id и все запросы с этим space передаются в экземпляр для отладки. space_id нужен также для того, чтобы иметь возможность запускать разные версии приложений. Один и тот же пользователь может обладать доступом к разным пространствам.

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

Вызов происходит, направляя соответсвующий запрос к объекту. У вызова есть 3 параметра:

  • api_name - название объекта
  • interface_name - интерфейс объекта
  • method_name - метод, который должен быть вызван

Интерфейс объекта - это стандарт, который может реализовывать объект. Каждый объект должен одинаковым образом реализовывать один и тот же стандарт. Один из примером стандарта - CRUD.

Схема работы


Рисунок - Структура системной и внешней шин

В языке BAY определено две шины: системная и внешняя. Системная шина используется для общения внутри backend системы. Между внутренними компонентами, микросервисами, которые всегда доступны. Общение может происходить через различные провайдеры: TCP, RabbitMQ, D-BUS, HTTP JSON, GRPC, SOAP, Message Pack. Вариант реализации провайдера системной шины в разных бэкендах и системах может быть разный. Системная шина может обращаться к api внешней шины. При этом user_id у нее должен быть равен -1000.

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

Аутентификация в системной шине происходит через rabbitmq, grpc и т.п.

К системной шине подключается Bus Gateway. Это API шлюз доступа к системе. API шлюз это фаервол. Он проверяет правильность входного пакета, авторизацию пользователя, сессии и в зависимости от результата отклоняет запрос или передает его в шину. API шлюз может также кэшировать некоторые API запросы, если в этом есть необходимость. Также шлюз может отвечать за раздачу статических файлов. Шлюзов в системе может быть несколько. Они могут обеспечивать балансировку нагрузку системы. Шлюзом может быть Nginx, написанный на OpenResty. С Bus Gateway общаются внешние компоненты: фронтенд пользователя, IoT устройства, внешние облачные системы.

Также системные запросы могут обрабатываться через шлюз Bus Gateway, но тогда должна:

  • производиться проверка по IP адресам.
  • проверка времени, когда был послан запрос.
  • обязательно запросы должны быть подписаны ключом. Параметр sign в теле запроса. Формат подписи определяется провайдером данных.

Чтобы подключить две системы друг к другу более тесно, лучше использовать Bus Connector.

Внешняя шина - это шина взаимодействия Шлюза (Bus Gateway) и внешних систем. К внешним системам относятся: фронтенд, мобильное приложение, IoT устройства, внешние облачные системы, интранет системы, 1С, сайты, агрегаторы и т.д. Внешняя шина может работать через разные провайдеры: HTTP JSON, GRPC, SOAP, Message Pack, XML-RPC и т.п.


Рисунок - внешняя шина

Примеры HTTP запросов к шлюзу Bus Gateway

Точка входа запроса для внешней шины:

https://bus-gateway/api/app_name/<api_name>/<interface_name>/<method_name>

Точка входа запроса для системной шины:

https://bus-gateway/bus/app_name/<api_name>/<interface_name>/<method_name>

При системном вызове параметр запроса "kind" должен быть равен "system", при внешнем - "user".

Подключение двух шин друг к другу

Возможно подключение двух системных или нескольких шин друг к другу. Чтобы это сделать, нужно использовать BUS Connector. В коннекторе требуется указать адреса подключаемых шин. Коннектор сканирует шины на наличие приложений. Приложения в двух шинах должны быть уникальны. После сканирования Коннетор начинает слушать сообщения в двух шинах. И если там возникает сообщения для приложения из другой шины, то он перенаправляет запрос в другую шину и возвращает ответ. Bus Connector требует наличия постоянного соединения к шинам.


Рисунок - Подключение двух шин

Формат запроса и ответа

Реализация структуры MessageRPC.bay

Запрос

{
  "time": 0,
  "message_id": "",
  "case": "rpc",
  "kind": "user",
  "to": "",
  "from": "",
  "volume_id": "",
  "locale": "",
  "api_name": "",
  "space_name": "",
  "method_name": "",
  "data": {},
  "jwt": "",
  "sign": ""
}

Параметры:

  • time - время (unix time), когда было послано сообщения.
  • message_id - Уникальный идентификатор сообщения. По нему отправитель сообщения будет получать ответ.
  • case - формат сообщения (event – событие, rpc – выполнение метода, stream – потоки)
  • kind - тип сообщения (system – системное, user – пользовательское)
  • to - кому предназначено сообщение app_name
  • from - кто послал сообщение app_name
  • volume_id - слой данных (виртуальное пространство)
  • locale - в какой локали выдавать ответ
  • api_name - название объекта
  • space_name - название интерфейса объекта
  • method_name - название метода
  • data - передаваемые данные
  • jwt - токен авторизации для внешней шины
  • sign - подпись для системной шины, если провайдер не пользволяет делать авторизацию

Ответ

{
  "time": 0,
  "message_id": "",
  "case": "answer",
  "kind": "user",
  "to": "",
  "from": "",
  "volume_id": "",
  "api_name": "",
  "space_name": "",
  "method_name": "",
  "code": 0,
  "success_message": "",
  "error_message": "",
  "error_name": "",
  "error_trace": "",
  "response": {},
  "have_answer": false,
}

Параметры:

  • time - время (unix time), когда было послано сообщения.
  • message_id - Уникальный идентификатор сообщения. По нему отправитель сообщения будет получать ответ.
  • case - формат сообщения (answer - ответ)
  • kind - тип сообщения (system – системное, user – пользовательское)
  • to - кому предназначено сообщение (кто ранее отправил сообщение)
  • from - кто послал сообщение (кому ранее предназначалось сообщение)
  • volume_id - слой данных (виртуальное пространство)
  • locale - в какой локали выдавать ответ
  • api_name - название объекта
  • space_name - название интерфейса объекта
  • method_name - название метода
  • response - данные ответа на запрос
  • code - код ответа. > 0 – запрос успешен. < 0 – ошибка. 0 – неизвестное состояние
  • success_message - положительный результат
  • error_message - сообщение об ошибке
  • error_name - имя ошибки (Обычно название класса Exception)
  • error_trace - трассировка ошибки, если включен debug у приложения
  • have_answer - backend ответил на запрос

Пример запроса

Запрос ping - это запрос проверки состояния системы healthcheck. Посылается запрос ping с текущим временем, и система должна вернуть это время. Вместо <app_api_name> ставиться системное название приложения, или self.

URL: https://bus-site-gateway/api/<app_api_name>/Ping/default/ping

"kind": "user",
"data":
{
  "time": 1234
}

Ответ:

"kind": "user",
"code": ERROR_OK,
"response":
{
  "time": 1234
}

Ошибки

Более подробный список ошибок в этом файле

/* Возможно запрос даже не обрабатывался */
static const int ERROR_NULL = 0;

/* Запрос успешно обработан */
static const int ERROR_OK = 1;

/* Запрос все еще обрабатывается */
static const int ERROR_PROCCESS = 100;

/* Неизвестная ошибка */
static const int ERROR_UNKNOWN = -1;

/* Просто ошибка */
static const int ERROR_FALSE = -100;

/* Фатальная ошибка */
static const int ERROR_FATAL = -99;

/* Объект не найден */
static const int ERROR_OBJECT_DOES_NOT_EXISTS = -5;

/* Такой объект уже существует */
static const int ERROR_OBJECT_ALLREADY_EXISTS = -6;

/* Ошибка валидации данных */
static const int ERROR_VALIDATION = -12;

/* Ошибка сериализации данных */
static const int ERROR_PARSE_SERIALIZATION_ERROR = -14;

/* Ошибка авторизации */
static const int ERROR_AUTH = -16;

/* Был добавлен дубликат данных */
static const int ERROR_DUPLICATE = -17;

/* Api не найдено */
static const int ERROR_API_NOT_FOUND = -18;

/* Ошибка потери соединения коннектором */
static const int ERROR_CONNECTOR_LOST_API = -19;