Так все-таки RAML или OAS (Swagger)?

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



    Пост подготовили Анна Мелехова и Владимир Лапатин

    Микросервисы. При разработке Acronis Cyber Cloud мы поняли, что нам никуда от них не деться. А проектирование микросервиса невозможно без формализации контракта, который представляет собой интерфейс микросервиса.

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

    image
    Схема мкросервисов Amazon из твита Вернера Вогелиса, СTO Amazon
    В чем же состоит дилемма? Де факто есть два способа взаимодействия микросервисов – HTTP Rest и gRPC от компании Google. Не желая быть вовлеченными в стек технологий Google, мы выбрали HTTP Rest. Аннотации к контрактам HTTP REST чаще всего описываются одним из двух форматов: RAML и OAS, ранее известный как Swagger Поэтому каждая команда разработчиков сталкивается с необходимостью сделать выбор в пользу одного из стандартов. Но, как выяснилось, сделать этот выбор может быть очень непросто.

    Зачем нужны аннотации?


    Аннотация нужна для того, чтобы внешний пользователь мог легко разобраться, что можно делать с вашим сервисом через его HTTP-интерфейс. То есть на базовом уровне аннотация должна содержать как минимум список доступных ресурсов, их HTTP-методов, тела запросов, перечисление параметров, указание необходимых и поддерживаемых заголовков, а также кодов возврата и форматов ответов. Крайне важным элементом аннотации контракта является и их словесное описание(“что будет, если добавить этот query-параметр к запросу?”, “в каком случае вернется код 400?”)

    Тем не менее, когда речь идет о разработке большого количества микросервисов, хочется извлекать дополнительную пользу из написанных аннотаций. Например, на основе RAML/Swagger можно генерировать и клиентский, и серверный код на огромном количестве языков программирования. А еще можно автоматически получать документацию к микросервису и заливать ее на ваш developer-portal :).


    Пример структурированного описания контракта

    Реже встречается практика тестирования микросервисов на базе описаний контрактов. Если вы написали и аннотацию, и компонент, то можно создать автотест, проверяющий адекватность работы сервиса с различными типами данных на входе. Не возвращает ли сервис код ответа, не описанный в аннотации? Сможет ли корректно обработать заведомо неверные данные?

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

    Для работы дополнительных инструментов и RAML, и OAS имеют возможность добавления метаданных, не предусмотренных стандартом (например, так это делается в OAS).

    В общем, поле для творчества в применении контрактов для микросервисов — огромное… по крайней мере теоретически

    Сравнение ежа с ужом


    В настоящее время приоритетное направление разработки в Acronis – развитие Acronis Cyber Platform. Acronis Cyber Platform – это новые точки интеграции сторонних сервисов с Acronis Cyber Cloud и агентской частью. Хотя наши внутренние API, описанные в RAML, нас устраивали, необходимость публикации API опять подняло вопрос выбора: какой же стандарт аннотаций лучше использовать для нашей работы?

    Изначально казалось, что решений два — это наиболее распространенные разработки RAML и Swagger (или OAS). Но по факту оказалось, что альтернатив как минимум не 2, а 3 или больше.

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

    Но разработчик RAML, компания Mulesoft, присоединилась к консорциуму Open API, который занимается развитием Swagger. Поэтому RAML приостановил свое развитие. Чтобы вообразить формат события представьте, что мейнтейнеры основных компонентов Linux ушли работать в Microsoft. Такая ситуация создает предпосылки для того, чтобы использовать Swagger, который динамично развивается и в последней — третьей версии — практически догоняет RAML по гибкости и функциональности.

    Если бы не одно но…


    Как оказалось, далеко не все open-source утилиты обновились до версии OAS 3.0. Для микросервисов на Go самым критичным будет отсутствие адаптации go-swagger под свежую версию стандарта. Однако разница между Swagger 2 и Swagger 3 — огромна. Например, в третьей версии разработчики:

    • улучшили описание схем аутентификации
    • доделали поддержку JSON Schema
    • прокачали возможность добавления примеров

    Ситуация получается забавная: при выборе стандарта нужно рассматривать RAML, Swagger 2 и Swagger 3 как отдельные альтернативы. При этом только Swagger 2 имеет хорошую поддержку инструментария OpenSource. RAML – очень гибкий… и сложный, а Swagger 3 слабо поддерживаются коммьюнити, так что вам придется пользоваться инструментами собственной разработки или коммерческими решениями, которые, как правило, стоят весьма дорого.

    При этом если в Swagger существует много приятных возможностей, таких как готовый портал editor.swagger.io, на который можно загрузить аннотацию и получить ее визуализацию с подробным описанием, ссылками и связями, то для более фундаментального и менее дружелюбного RAML такой возможности нет. Да, можно поискать что-то среди проектов на GitHub, найти там аналог и самостоятельно его развернуть. Однако в любом случае кто-то должен будет поддерживать портал, что не так удобно для базового использования или тестовых нужд. Кроме того, swagger более “беспринципен”, ну или либерален — его можно генерировать из комментариев в коде, что, конечно, идет вразрез с принципом API first и не поддерживается ни одной из утилит RAML,

    Мы в свое время начали работать с RAML, как с более гибким языком, и в итоге много должны были делать своими руками. Например, в одном из проектов используется утилита ramlfications в юнит-тестах, которая поддерживает только RAML 0.8. Так что пришлось добавлять костыли, чтобы утилита смогла “кушать” RAML версии 1.0.

    А нужно ли выбирать?


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

    Для решения этой задачи существует два инструмента OpenSource, которые должны обеспечивать конвертацию контрактов:

    1. oas-raml-converter – ныне неподдерживаемая утилита. В процессе работы с ней мы обнаружили, что у нее возникает ряд проблем со сложными RAML’ами, которые “размазаны” по большому количеству файлов. Эта программа написана на JavaScript и выполняет рекурсивный обход синтаксического дерева. Из-за динамической типизации разобраться в этом коде становится сложно, так что мы решили не тратить время на написание патчей к умирающей утилите.
    2. webapi-parser — инструмент от той же компании, который претендует на готовность конвертировать все и вся, причем в любую сторону. На сегодняшний день заявлена поддержка RAML 0.8, RAML 1.0 и Swagger 2.0. Однако на момент нашего исследования утилита была еще КРАЙНЕ сырой и непригодной для использования. Разработчики создают своего рода IR, что позволит им в будущем быстро добавлять новые стандарты. Но пока все это просто не работает.

    И это еще не все сложности, с которыми мы столкнулись. Одним из шагов нашего пайплайна является проверка того, что RAML из репозитория является корректным относительно спецификации. Мы перепробовали несколько утилит. Удивительно, но все они ругались на наши аннотации в разных местах и совершенно разными дурными словами. Причем не всегда по делу :).

    В конце концов мы остановились на ныне устаревшем проекте, который также имеет ряд проблем (иногда падает на ровном месте, имеет проблемы при работе с регулярными выражениями). Таким образом, мы не нашли способа решить задачи валидации и конвертации на базе бесплатных инструментов, и решили пользоваться коммерческой утилитой. В будущем, когда OpenSource средства станут более развитыми, решение этой задачи, возможно, станет проще. А пока трудо-временные затраты на “допиливание” показались нам более значительными, чем стоимость коммерческого сервиса.

    Заключение


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

    Определить набор инструментов, которые вы будете использовать позже. Например, если вам нужно просто отображать контракт, проще будет использовать Swagger 2, у которого есть красивый API, ведь в RAML вам придется поднимать и поддерживать сервис самостоятельно.
    Чем больше будет у вас задач, тем шире будет потребность в инструментах, а они разные для разных платформ, и лучше сразу ознакомиться с доступными версиями, чтобы сделать выбор, минимизирующий ваши затраты в будущем.

    Но стоит признать, что все экосистемы, существующие на сегодняшний день, несовершенны. Поэтому если в компании есть фанаты, которые любят работать в RAML, потому что “он позволяет более гибко выражать мысли”, или, наоборот, предпочитают Swagger, потому что “он понятнее”, — лучше всего оставить их работать в том, в чем они привыкли и хотят, потому что инструментарий любого из форматов требует доработки напильником.

    Что касается нашего опыта, в следующих постах мы расскажем о том, какие — статические и динамические проверки мы проводим на базе нашей RAML-Swagger архитектуры, а также о том, какую документацию мы генерируем из контрактов, и каким образом все это работает.

    Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

    А какой язык вы используете для аннотаций контрактов микросервисов?

    Acronis
    162,95
    Компания
    Поделиться публикацией

    Комментарии 28

      +2
      Странно, что исключён из рассмотрения, пожалуй наиболее очевидный и мощный протокол: веб-сервисы (на транспорте SOAP) и его язык описания WSDL. Который, как минимум, на порядок более широко используемый и удобный чем gRPC
        +3
        WSDL, конечно, мощный протокол. Но кажется его перспективы развития чуть слабее чем у RAML. И исторически WSDL относится больше к enterprise разработке, а не к облачным сервисам, которые преимущественно HTTP REST или gRPC.
          0
          Я, как инженер, больше привык привык оперировать техническими терминами и что такое «enterprise» разработка в контексте рассмотрения вариантов протоколов мне не совсем понятно. Выглядит как навешивание ярлыков.

          И очень хотелось бы узнать в каком именно месте развитие слабее.

          Вот вам пример: вы присылаете в сервис несколько бинарных блобов вместе с их путями, степень и тип сжатия.
          Вам надо запаковать эти блобы под их путями в архиве и выдать клиенту блоб представляющий архив.
          Как бы вы это сделали с помощью вашего RAML и REST?
            0
            Я, как архитектор, привыкла что привычки людей тоже входят в часть уравнения. Что при принятии решения о выборе технологий учитывается тот стек, который уже в ходу. И оттуда возникает вопрос про «enterprise» или «cloud».

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

              Ну вот: тулзы это уже конкретика, их как раз и можно было бы сравнить.

              А по поводу «enterprise» или «cloud» у меня возникает только один единственный вопрос: что означают эти термины в вашем понимании и в чём между ними отличие?

              Вот, к примеру, сервисы Amadeus (глобальной системы бронирования авиабилетов) это «enterprise» или «cloud»?
          +1
          Вот я работаю и с тем и с другим. Да, веб-сервисы намного мощнее, но и тяжелее тоже на много! REST гораздо проще, работать с ним приятнее и в 90 процентов случаев его хватает. И по моим наблюдениям он потихоньку вытесняет WS даже из кровавого энтерпрайза — сужу по чешским банкам.
            0
            Я тоже работал и с тем и другим. Мне, наоборот, показалось SOAP на порядок проще, особенно если используется Java — сервер и Java — клиент
              0
              Ну как сказать, плагин в POM добавь, interceptor-ы с логированием напиши, инициализация сервиса с lazy, а иначе все свалится в момент старта если веб-сервис недоступен, всякие bla.getArrayOfBlaBla.getValues() в коде. Это только касается клиента.
              А если сервера, то там нас ждет еще XSD схема. На такое даже после WCF SOAP без слез не взглянешь.
                0
                Не путайте народ. Ничего этого не нужно в случае Java-сервера и Java-клиента.

                Достаточно проаннотировать интерфейс и импелементировать для сервера.
                А на клиенте получить прокси, для того же интерфейса.

                Для этого даже ни одной библиотеки не надо подключать — всё доступно в голой JDK с версии 6. Можно запускать серверную часть без всякого сервера и Spring Boot — ов.
                Вот это я понимаю: настоящий микросервис
                  0
                  Кажется у вас это не SOAP, а RMI.
                    0
                    Кажется у вас это не SOAP, а RMI.


                    Нет, читайте мануалы
                +1
                Как я понял из ваших комментариев мы все как-то не так WS используем :) Ну значит вот вам еще один повод использовать вместо него REST — WS SOAP можно использовать разными способами и куча людей его неправильно «готовит» :) А с REST все примитивно.
                  0

                  Да не сказал бы, что всё примитивно. Одни статусы ответа чего стоят. Всегда поиск компромиссов
                  Или передача параметров запроса, тоже мутный вопрос: делать таки гигантский get, или всё-таки post с телом, в который положить все параметры запроса
                  В rest очень много тонких мест, которые реализуются по разному в разных командах

              0
              Много накладных расходов. Код читается сложнее, нежели JSON.
              Иногда возникают ситуации, когда надо сидеть и читать сам WSDL, а это тоже занятие на любителя.
              Опять же, были ситуации, когда SOAP сервис не мог дать свыше 50 RPS в сокете. И разработчики сервиса в принципе не понимали, с чем связано это ограничение. Оно от них скрыто 50 уровнями абстракции.
                –1
                Учитывая этот и ваш комментарий выше у меня сложилось впечатление что вы используете веб-сервисы очень странным образом. Рекомендую прочитать мануалы по jax-ws.
                  0
                  Altova/Oxygen — да стоят денег, не ХАЛЯВА.
                  На ХАЛЯВУ в текстовом редакторе.
                  0
                  Слишком гибкий и не однозначный.
                  Плюс имплементация на Java и у MS могут быть небольшие разночтения.
                  А если в wsdl добавят «Any», то концов не найдешь.
                  Гарантированно куча приключений.
                  Плюс для работы нужно из wsdl/xsd генерировать классы, с кучей аннотаций.
                  Хорошо если автогенерированные классы сразу подойдут.
                  Если нет, то надо еще плясать с Jaxb-bindings.
                  В этом плане JSON (REST), это то что надо.
                  Все просто.
                  POJO не нужны аннотации в 99% случаев.
                  Есть удобный серриализатор/дессериализатор Gson.
                  Спасибо.
                  Я уже вдоволь наработался с SOAP сервисами. :-)
                  +1
                  Я пропустил выход Swagger 3? Или авторы традиционно его спутали с OAS 3? Месяц назад была нерабочая альфа по крайней мере.
                    0

                    Основываясь на документации с сайта swagger.io(раз, два), мы воспринимали Swagger как старое название OAS. Релиз третьей версии — это OAS3, релиз второй версии — это Swagger 2, но OAS3 — это развитие swagger2. То есть формально Swagger 3 не существует, равно как и OAS2. Но это казалось усложнением в статье, поэтому мы их прировняли. Ты ведь на это ссылаешься? Или есть какие-то отдельные ветки эволюции и Swagger 2 породил не только OAS3, но и swagger 3?

                    –2
                    Де факто есть два способа взаимодействия микросервисов – HTTP Rest и gRPC от компании Google. — Чушь. Многие используют WS-SOAP/graphql/OData/cql — то что подходит лично им и конечно асихронный вещи типа AMQP/ZeroMQ/RabbitMQ/MaaS и прочие *MQ, где обычно RPC… etc

                    Но разработчик RAML, компания Mulesoft, присоединилась к консорциуму Open API, который занимается развитием Swagger. Поэтому RAML приостановил свое развитие. — что тут сказать, выбрали Open Source и… Вам не повезло… sorry.
                    Кто ответственный за выбор RAML? Кто сжёг кучу денег инвесторов?

                    В чем же состоит дилемма? — то, что сервисы между собой общаются, а также имеют общие «объекты» и должны одинаково выполнять определённые функции — типа логирования/мониторинга.

                    задумываться об оптимизации процесса — Должен быть выстроен нормальный CI и версионирование, когда добавление условного «нового поля» которое нужно двум сервисам не требует труда разработчиков (тех сервисов, котрым это поле не требуется в логике), тоесть правка контракта, далее нажали «кнопочку» пошла пересборка/вынос/переподписка на новую версию сервиса.

                    При этом если в Swagger существует много приятных возможностей, таких как готовый портал editor.swagger.io — сегодня он есть, а завтра 404.

                    стоят весьма дорого. — Altova XMLSpy from € 799.00, так понимаю при выборе RAML — это было дорого, сколько будет стоить миграция на swagger или OAS3?

                    Для большой компании такие затраты терпимы, но для стартапа могут стать большой нагрузкой. — Для стартапа не нужны RAML/Swagger… etc — достаточно синхронного REST (или асинхронной эмуляции) и странички в confluence за 10$.

                    Нашаманившись с дописыванием экосистемы решений под RAML — при переходе на Swagger или OAS3 с вашими требованиями отнють не StartUp_а, начинайте с написания генератора — c нуля или вперёд править открытые по 5 лет issue и заниматься пропихиванием их в upstream --> Добро пожаловать в Open Source.

                    а также кодов возврата и форматов ответов. — только не забудьте написать, что возможно получить в ответ любые коды http протокола и как их отличать от «бизнес ошибок» или не получить если их по какой-то причине «сожрёт» какой-нибудь proxy-сервер по дороге.
                      0
                      Упомянутый картинкой Amazon в API Gateway предлагает как раз Swagger/OpenAPI 3.
                      Для генерации необходимого документа использую io.swagger.core.v3:swagger-maven-plugin, который понимает jakarta.ws.rs:jakarta.ws.rs-api аннотации на REST интерфейсах с помощью io.swagger.core.v3:swagger-jaxrs2
                      0
                      При этом только Swagger 2 имеет хорошую поддержку инструментария OpenSource. RAML – очень гибкий… и сложный, а Swagger 3 слабо поддерживаются коммьюнити, так что вам придется пользоваться инструментами собственной разработки или коммерческими решениями, которые, как правило, стоят весьма дорого.

                      На мой взгляд, наоборот, community очень активно поддерживает и расширяет как AOS 3, так и open-source проекты под неё.


                      Например, для генерации клиентского кода на C# или TypeScript есть расширение для Visual Studio 2017/2019Unchase OpenAPI Connected Service, которое позволяет легко генерировать proxy-классы для взаимодействия с OAS и Swagger 2 с настройкой различных параметров (аналогично NSwagStudio). Достаточно иметь (сгенерировать или создать вручную, в т.ч. с помощью специализированных online-редакторов) endpoint URI (URL, json- или yaml-файл спецификации).


                      Как им пользоваться можно прочитать здесь: How to generate C# or TypeScript client code for OpenAPI (Swagger) specification.

                        0
                        Swagger (swagger2) имеет мощную инструментальную базу open source. При этом OAS3 в большей части тулзов, что мы пробовали, имеет либо экспериментальную поддержку либо не поддерживается вообще. Перефразируя, OAS3 — это мейнстрим, но пока число тулзов поддерживающих OAS3 значительно меньше чем число тулзов, поддерживающих swagger2
                          0

                          Только потому что любая новая технология (или её последующий виток развития) должна обзавестись необходимой поддержкой. Собственно, дело времени.

                        +1

                        зашёл в статью почитать как люди готовят REST и наверное не нашёл то, чего искал – понимания почему его до сих пор горячо любят и активно используют…


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


                        у REST-а же вечные споры какой код ошибки правильно послать на ту или иную ошибку. под знаменем с {"success":false} марширует толпа вордпрессеров. а как правильно засадить фильтры и сортировки в выборки. а коммент к посту – это /comment/:id или /post/:post_id/comments/:comment_id – let the battle begin. и снова отсутствие стандарта на пуши через веб-сокеты…


                        я наверное что-то упустил, но с REST в проектах я постоянно вижу какую-то сборную солянку и кустарщину. может мне просто не везёт?… или просто я смотрю на REST с клиентской стороны, а для внутреннего общения микросервисов вот это всё – вообще не проблема?…

                          0
                          Все споры и «сборная солянка» решаются обычно API Guideline-ами. Пока не могу дать ссылку на наш, хотя мы тоже себе разработали (более практичный, менее объемный). Поэтому вот MS-ный github.com/Microsoft/api-guidelines/blob/vNext/Guidelines.md
                          Отвечая же на Ваше сравнение с GraphQL — как архитектор вы всегда в поисках компромисса: жесткий стандарт не всегда уместно ограничивает и порождает уродливые конструкции, гибкое и более высокоуровневое описание дает возможность как написать очень хорошо, так и написать очень плохо…

                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                        Самое читаемое