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

Создание RPC-модулей

1. Описание

RPC (Remote Procedure Call) — это протокол взаимодействия между клиентом и сервером, позволяющий вызывать процедуры или функции на удалённом сервере так, как если бы они выполнялись локально. Этот подход используется при разработке распределённых и масштабируемых приложений, где отдельные компоненты взаимодействуют между собой, находясь на разных серверах или в разных сетевых средах.

RPC-модуль — компонент, обеспечивающий взаимодействие между сервисом Cloudlink и внешним сервисом. Для этого он использует RPC-вызовы удалённых функций.

Внутри инфраструктуры Cloudlink RPC-модуль подключается к заданной очереди RabbitMQ внутри Kubernetes-кластера. После разработки и и развертывания модуль можно интегрировать с Конструктором Cloudlink и использовать внешний сервис в качестве узла в графе.

2. Предварительные условия

Для использования внешнего сервиса или приложения совместно с Kонструктором Cloudlink нужно:

  1. Разработать rpc-модуль для интеграции с Конструктором Cloudlink.

  2. Добавить разработанный модуль в качестве шаблона узла в Конструктор Cloudlink.

  3. Создать граф для продукта.

3. Разработка RPC-модуля

Развёртывание RPC-модуля может выполняться двумя способами:

  • Внутри Kubernetes-кластера Cloudlink:

    RPC-модуль размещается в том же кластере Kubernetes, где работают все необходимые сервисы.

    schm kubernetes rpc 1
  • Вне Kubernetes-кластера:

    RPC-модуль разворачивается за пределами кластера. В этом случае необходимо обеспечить корректное подключение к RabbitMQ, State Service и другим сервисам внутри кластера.

    schm kubernetes rpc 2
    В качестве Service 1,2,3 может выступать любой компонент инфраструктуры Cloudlink.

4. Реализация RPC-модулей

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

Основные компоненты:

  1. Adapter

    • Базовый класс для адаптеров.

    • Используется для инициализации, выбора и выполнения методов интерфейса.

    • Используется для интеграции с внешними сервисами.

  2. BaseTaskReceiver

    • Класс для приема и обработки задач.

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

  3. TaskReceiver

    • Расширяет BaseTaskReceiver, интегрируя пользовательские адаптеры.

    • Использует методы адаптера для выполнения задач.

  4. RetryRequestByStatus

    • Декоратор для повторных попыток HTTP-запросов.

    • Используется для настройки задержек, таймаутов и обработки ошибок.

      Пример использования
      from app.adapter import NovaAdapter
      from app.settings import APP_NAME, TASK_EXECUTION_TIMEOUT
      from cld_rpc_utils.utils import TaskReceiver
      task_receiver = TaskReceiver(NovaAdapter, APP_NAME,
      TASK_EXECUTION_TIMEOUT)
      task_receiver.get_messages()

4.1. Процесс исполнения задачи с помощью библиотеки NovaAdapter

  1. Получение сообщения из RabbitMQ

    TaskReceiver прослушивает очередь RabbitMQ. При появлении нового сообщения (задачи) оно извлекается из очереди для дальнейшей обработки.

  2. Обработка сообщения

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

  3. Инициализация адаптера

    TaskReceiver создаёт экземпляр адаптера (NovaAdapter) и передаёт ему входные данные и параметры логирования.

  4. Выбор метода интерфейса

    В NovaAdapter вызывается метод obtain_interface_method(), который определяет, какой метод интерфейса использовать на основе входных данных.

  5. Выполнение задачи

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

  6. Логирование и обработка ошибок

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

  7. Возвращение результата

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

  8. Ожидание следующей задачи

    После обработки текущего сообщения TaskReceiver продолжает прослушивание очереди RabbitMQ для получения новых задач.

4.2. Примеры использования библиотеки Nova Adapter

  1. Создание адаптера

    Адаптер должен наследоваться от базового класса Adapter. Инициализация адаптера включает базовую настройку и подготовку к работе с RPC-модулем.

    Пример инициализации адаптера для Nova Adapter
    pythonCopy code
    # app/adapter.py
    from cld_rpc_utils import Adapter
    from typing import Union
    from logging import Logger, LoggerAdapter
    from cld_py_logging.log_extra import ExtraLogger
    class NovaAdapter(Adapter):
    def __init__(self, inputs: dict, logger: Union[Logger, LoggerAdapter, ExtraLogger]):
    super().__init__(inputs, logger)
    # Здесь можно инициализировать внутренние переменные и состояние
    # Инициализация и настройка для работы с Nova
    async def create_nova(self):
    # Логика создания Nova
    # Другие специфические методы
  2. Инициализация клиента

    Клиент представляет собой компонент, который управляет взаимодействием с внешними сервисами или API. В этом контексте клиент используется для отправки запросов и получения данных от сервиса AWX.

    Пример кода для AWX Client:
    # rpc-awx/aioawxclient/client.py
    import logging
    from aiohttp import ClientSession, TCPConnector
    from cld_rpc_utils import RetryRequestByStatus
    from aioawxclient import api
    from .exceptions import AWXException
    from .settings import *
    from .utils import response_handler
    class Client:
    _REQUIRED_HEADERS = {"Content-type": "application/json"}
    
    def __init__(self, url: str, port: int, usern
    ame: str, password: str, organization_name, api_version="v2", logger=logging):
      self.logger = logger
      self.api_version = api_version
      self.api_root = f"{url}:{port}"
      self.username = username
      self.password = password
      self.organization_name = organization_name
      # Другие начальные настройки
    async def __aenter__(self):
    # Инициализация асинхронной сессии и аутентификации
    # ...
    # Другие методы для взаимодействия с AWX API

    Клиент можно интегрировать с адаптером через метод _init_client. Этот метод:

    • Устанавливает соединение с внешним сервисом (AWX).

    • Инициализирует все необходимые компоненты для работы клиента.

      Пример интеграции:
      class MyCustomAdapter(Adapter):
      def _init_client(self):
      self.client = AWXClient(
        RPC_AWX_SETTINGS.awx_api_endpoint,
        RPC_AWX_SETTINGS.awx_api_port,
        RPC_AWX_SETTINGS.awx_username,
        RPC_AWX_SETTINGS.awx_password,
        RPC_AWX_SETTINGS.awx_organization,
      logger=self.logger,
      )
      return self.client
      # Дополнительные настройки клиента, если необходимо

      В этом примере MyCustomAdapter инициализирует клиент AWX в методе _init_client. Такое разделение логики адаптера и клиента повышает читаемость и удобство поддержки кода.

  3. Реализация специфических функций

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

    Пример кода:
    async def create_nova(self):
    # Логика для создания Nova
    async def update_nova(self):
    # Логика для обновления Nova
    # ... другие методы ...
  4. Интеграция с TaskReceiver

    Используйте TaskReceiver для приема и обработки задач. Инициализируйте его с вашим адаптером ( NovaAdapter ) и настройками приложения.

    Пример кода ( rpc_nova.py ):
    # rpc_nova.py
    from app.adapter import NovaAdapter
    from cld_rpc_utils.utils import TaskReceiver
    from app.settings import APP_NAME, TASK_EXECUTION_TI
    MEOUT
    task_receiver = TaskReceiver(NovaAdapter, APP_NAME,
    TASK_EXECUTION_TIMEOUT)
    task_receiver.get_messages()
  5. Обработка задач

    В TaskReceiver реализован метод get_messages() , который начинает прослушивание и обработку входящих сообщений.

  6. Обработка и логирование

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

    Пример кода:
    async def create_nova(self):
    self.logger.info("Начало процесса создания Nova...")
    # Логика создания
    self.logger.info("Завершение процесса создания Nova...")
  7. Взаимодействие с внешними сервисами

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

    Пример кода:
    async def create_nova(self):
    # Вызов внешнего сервиса или API
    external_service_result = await some_external_service_call()
    # Обработка результатов

5. Создание шаблона узла для RPC-модуля

RPC-модуль подключается к Конструктору Cloudlink через настроенный узел в Control Panel → раздел Шаблоны узлов. Этот узел обеспечивает связь с необходимой очередью RabbitMQ для обработки запросов.

Подробнее о настройках Шаблонов узлов в Руководстве администратора Cloudlink.

Модуль можно развернуть как внутри Kubernetes, так и в другой среде. При этом важно обеспечить доступ к:

  • очереди RabbitMQ

  • State Service

  • остальным сервисам, с которыми модуль должен взаимодействовать.

Порядок действий
  1. Перейдите в раздел Control Panel Конструктор → Шаблоны узлов и нажмите +.

  2. Во вкладке Основное укажите следующие параметры:

    • Наименование

    • Код шаблона

    • Описание

    • Название очереди для старта задачи

    • Название очереди для отката

      Очередь для отката может быть пустой, если данный функционал не используется.
    • Время ожидания

    • Тип

      constructor template node 1

      Названия очереди для старта и отката задачи должны быть согласованы между разработчиком RPC-модуля и разработчиком продукта или графа. Для предварительного создания очереди, необходимо подключиться к RabbitMQ по ссылке https://rabbitmq.FQDN_Cloudlink/ и выполнить следующие действия:

      1. Перейдите на вкладку Queues и найти пункт Add a new queue.

      2. В качестве параметра Virtual host выберите корневой каталог /.

      3. Выберите значение параметра Type из следующих вариантов:

        • Default for virtual host

        • Classic

        • Quorum

        • Stream

      4. Введите название в поле Name. Оно должно совпадать с названием очереди в шаблоне узла.

      5. Укажите длительность сохранения очереди в параметре Durability.

      6. При необходимости указать дополнительные параметры в поле Arguments.

        queue1
  3. Во вкладке Параметры укажите входные и выходные параметры, которые нужны для работы вашего RPC-модуля.

    Указанные параметры передаются в виде сообщения при обмене данными между RPC-модулем и приложением.

    constructor template node 2

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

    constructor template node 3
  4. Нажмите Добавить.

Созданный шаблон узла вы можете использовать в конструкторе графа.

6. Использование RPC-модуля в качестве узла в графе

Порядок действий
  1. Перейдите в раздел Конструктор → Графы.

  2. Нажмите кнопку +.

  3. В вкладке Общая информация введите следующие параметры:

    • Код графа — укажите уникальный символьный идентификатор продукта. Он может содержать только прописные латинские символы, цифры, нижнее подчеркивание,тире двоеточие и точку.

    • Наименование — дайте уникальное название создаваемому графу.

      Код графа,наименование и автор необходимы для идентификации и управления графом в системе.
    • Тип —  укажите один из двух типов acting или creating.

    • Описание(опционально) — опишите отличительные особенности продукта.

    • Автор — укажите автора продукта.

    • При необходимости активируйте опции, которые включаются в случае некорректного проигрывания графа:

      • Переводить заказ в статус "Ошибка".

      • Блокировать заказ при ошибке.

        constructor graph 1
  4. Добавьте узлы графа:

    1. Перейдите во вкладку Узлы и нажмите кнопку +.

    2. На вкладке Основное укажите следующие параметры:

      • Название

      • Описание

      • Выберите Шаблон узла с RPC-модуля.

        constructor graph 2
      • Перейдите на вкладку Параметры и введите данные:

        • В блок кода Static data.

        • В блоках кода Input и Output будут указаны входные и выходные переменные, которые были ранее указаны при создании Шаблона узла. При необходимости их можно отредактировать.

          Данные, указанные в Static data можно использовать во всем графе. *
          constructor graph 3
      • На вкладке Дополнительное заполните параметры, касающиеся запуска узла.

  5. Нажмите Добавить, чтобы завершить создание узла.

    Узел будет добавлен в граф.

    constructor graph 4
  6. Перейдите на вкладку графа Параметры заказа и задайте параметры:

    • В блоке Форма заказа опишите структуру в JSON-формате для определения структуры данных и параметров заказа:

      constructor graph 5
      Пример 1. Описание параметров в структуре JSON

      Здесь создаётся новый объект с наименованием New app. Для него в форме заказа должны быть заданы следующие поля: vars, credentials, net_segment.

      • Поля vars и credentials имеют строковый тип.

        • Значения задаются по шаблону (pattern): "^[a-zA-Z0-9-]*$ (латинские буквы в верхнем и нижнем регистре, цифры от 0 до 9, символы - и ).

        • Ограничения по длине:

          • минимальное количество символов — 3 ("minLength": 3),

          • максимальное количество символов — 64 ("maxLength": 64).

      • Параметр net_segment имеет тип object и позволяет выбирать значения из списка.

      Для создания пользовательского интерфейса формы заказа используется UI-схема.

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

      constructor graph 6
      Пример 2. Переопределение виджетов и полей в интерфейсе UI-схемы

      Для полей vars и credentials свойство ui:widget задаёт тип отображения элемента формы. В данном случае используется "ProductLabelWidget", который отображает поле как текстовую метку с названием продукта.

      Для поля net_segment свойство ui:field определяет интерфейс для работы со связанными объектами. Значение "DirectoryUiListField" в пользовательском интерфейсе будет выглядеть как поле со списком значений с доступными шаблонами на платформе.

    • Перейдите на вкладку Детализация цены и выберите продукт для расчета стоимости и получите детализацию заказа.

  7. Во вкладке Модификаторы выберите тип среды для исполнения графа:

    • dev

    • prod

    • test

  8. Нажмите Сохранить, чтобы завершить создание графа.