Intro
Для начала, обозначу рассматриваемые вопросы данной статьи:
- использование системы 1С как одного из сервисов внутри компании;
- реализации Contract First подхода при написании http-сервисов;
- tracing запросов - что это такое и для чего нужно 1С разработчику.
Так же, будет продемонстрировано использование "самописной" конфигурации, реализующей пример решения рассматриваемых вопросов. Данная конфигурация не является готовым продуктов и распространяется как есть. Скачать можно на GitHub. Используемая платформа 8.3.18.
Ich will
Что бы понять требования к одному из сервисов внутри компании, давайте составим общий "портрет" любого из сервисов внутри компании и совместим с возможностями прикладного решения на платформе 1С.
Очевидным будет признать - большинство популярных фреймворков разработки сервисов ориентированы на архитектурный стиль REST API. А самым используемым форматом обмена данными является JSON. Платформа 1С позволяет нам как создавать собственные http-сервисы, так и имеет средства работы с JSON. Фиксируем: REST API и JSON.
Далее, определим как мы будем документировать созданный сервис для себя и предоставлять описание сервиса другим разработчикам. Это должно быть универсально и всем понятно. Таким стандартом де-факто является спецификация OpenAPI, актуальной версии 3.0.2. Сама спецификация представляет из себя либо JSON либо YAML файл. Создать абстрактный JSON файл платформа 1С позволяет, а следовательно вопрос создания спецификации лежит только в плоскости ее наполнения. Для работы со спецификациями наиболее распространен набор инструментов Swagger, включающий в себя пользовательский интерфейс Swagger-UI для визуализации OpenAPI. Swagger-UI можно встроить как отдельный ресурс http-сервиса. Запоминаем: OpenAPI и Swagger-UI.
Спецификация сервиса должна быть сопуствующим артефактом работающей системы, но вот подходов к его появлению несколько: Contract First и Code First. Первый подход говорит нам - сначала разработай спецификацию (заключи контракт) и уже по ней пиши код, но и пока не изменится спецификация изменения в коде не должны нарушать заключенный контракт. А вот второй подход ровно противоположный - пиши код и спецификации будет сгенерирована на его основании, т.е. изменение кода ведет к обновлению спецификации. Кодогенерацию спецификации поддерживает большинство фреймворков, поэтому Code First подход довольно распространен. Для платформы 1С подход Code First реализовать возможно только на уровне вендора, но вот для прикладных решений такой возможности совсем нет. Нам остается Contract First.
И самым важным, по моему мнению, требованием будет возможность наблюдать работу сервисов - частота вызова, среднее время отклика, наличие ошибок и многое другое. Теория "Наблюдаемости" (Observability) систем нам подсказывает очевидный выход - Tracing запросов. Для его реализации есть универсальные Jaeger или Elastic APM. Я хочу Elastic APM.
Ich will dass ihr mir vertraut
Любую архитектурную проблему можно решить добавлением дополнительного слоя абстракции, кроме проблемы большого количества абстракций.
При создании http-сервиса в конфигурации кроме проблемы ограниченности настроек нашего сервиса еще существует проблема невозможности эти настройки получить в рамках функции платформы. Поэтому подход Contract First будет реализован созданием нового уровня абстракции. От созданного http-сервиса нужна только одна точка входа на любой вызов, а вот логика его обработки будет в отдельной подсистеме. К слову говоря, уже есть подобное решение, но ключевым отличием от него является способ заключения контракта.
Созданный http-сервис с настройками:
Как мы видим, для сервиса достаточно единой точки входа:
ОбработкаВызоваREST.ОбработатьЗапрос(Запрос);
Подготовка задачи
Что бы наглядно провести демонстрацию создания REST API поставим простую задачу - реализация CRUD функций для абстрактного справочника "Склады" с авторизацией для функций изменения данных.
Подготовим нашу конфигурацию. Добавим данный справочник и кроме стандартных реквизитов добавим еще "ВидСклада" с типом перечисление. В нем будет достаточно два значения: "Оптовый" и "Розничный". Так же будет необходим общий модуль "ИнтеграцияСклад", где будут размещения обработчики логики вызываемых запросов. В модуле разместим заготовки методов:
Созданный справочник и перечисление:
Произведем публикацию базы на веб-сервере под именем test.
Настройка сервиса
Кратко пробежимся по основным настройкам нового сервиса.
Шаг первый - регистрация самого сервиса.
Шаг второй - определим наши ресурсы. Исходя из поставленной задачи, у нас должны быть:
- /warehouses - общий с методами GET (полный список складов) и POST (создание нового склада)
- /warehouses/{id} - адрессный с методами GET (получить конкретный склад), PUT (модифицировать существующий) и DELETE (пометить на удаление)
Данные ресурсы разместим в группе logistic. Параметр URL "id" потребует краткого описания.
Шаг третий - необходимо предопределить модели данных, используемые в нашем сервисе. В рамках примера сделаем упрощенную схему с двумя моделями, где разница только в наличии отдельного поля "Код" склада. Плюс опишем перечисление "ВидСклада" как отдельную схему данных.
Шаг четвертый - описать методы ресурсов. Для каждого метода описываем необходимые параметры - наличие авторизации, модели данных тела запроса, варианты ответа и его модель данных при наличии тела ответа.
Заключительный шаг - активировать произведенные настройки для обработки вызовов. Используем соответствующую команду "Сохранить настройки". Теперь мы можем перейти по ссылке http://localhost/test/hs/internal/v1/docs и увидеть сгенерированную спецификацию. Данный пример наглядно показывает подход Contract First в рамках системы 1С.
Ich will dass ihr mir glaubt
Важным моментом в разработке по подготовленной спецификации является разделение ответственности за входящие данные запросов и логику обработки этих запросов. Программист должен писать обработчик утвержденного метода ресурса без отвлечения на проверку входящих параметров или объекта тела запроса. Эту функцию возьмет на себя представленная разработка.
Нарушение спецификации со стороны клиента приводит к коду ответа 422, согласно стандарта RFC. Нарушение спецификации со стороны сервера (ошибка программиста) будет приводить к ответу 500. Так же, данный код ответа будет формироваться при любом исключении, возникающим при вызове функции-обработчика. Поэтому любые описанные в нашей спецификации API методы ресурсов будут содержать данные варианты ответа по умолчанию.
Проверим функцию контроля спецификации со стороны клиента на примере метода создания склада. Пропустим в теле запроса свойство "type" и получим соответствующую ошибку от сервера. Самое главное, сама функция-обработчик метода не была вызвана, а клиент получил человекочитаемое описание ошибки.
Ich will eure Blicke spüren
Для рассмотрения трейсинга запросов нам потребуется установленные Elasticsearch и Kibana версии 7.17, а так же APM server соответствующей версии. Будем считать что они все работают на localhost. Подробное описание принципа работы, развертывания и настройки данного окружения опущено, так как не является темой данной статьи.
Доработаем код модуля "ИнтеграцияСклад", сделав работоспособными методы ресурса /warehouses (получить список складов и создать новый).
Сделаем несколько запросов к сервису. При правильно настроенном окружении мы увидим примерно следующую картину в Elastic APM:
Инструмент мониторинга увидел нашу систему 1С как сервис. Кроме общего дашборда имеется статистика вызовов по каждому методу вызываемых ресурсов и зарегистрированные ошибки кода 1С.
Ich will jeden Herzschlag kontrollieren
Кроме общего мониторинга запросов к сервису, трейсинг дает нам возможность получить информацию о каждом запросе. Кто его вызвал, с какими параметрами, какой ответ вернул сервер. А самое главное, при определенных доработках кода, мы можем видеть распределение времени выполнения по обозначенным участкам кода обработчика.
Результат "из коробки" нашей конфигурации показывает запрос как одно целое. Что бы добавить span внутри запроса сервиса используем встроенные методы подсистемы.
ЛогированиеREST.ИнициализироватьИнтервалОбращенияКБД(КонтекстЗамера, Наименование, Запрос);
ЛогированиеREST.ЗавершитьИнтервал(КонтекстЗамера);
Запросом к БД является не только выборка через объект "Запрос", но и работа в объектной модели - чтение и запись объекта. Проведем небольшую иньекцию в наш код.
Повторим несколько запросов к сервису и посмотрим результат.
Gitarrenpart
Подведем краткий итог:
- систему 1С можно и нужно использовать как один из сервисов внутри компании;
- подход Contract First реализуем и и повышает качество написания http-сервисов;
- tracing запросов должен стать базовым инструментом разработчика 1С.
Буду рад обратной связи и спасибо за потраченное время!
Обработчики