Pull to refresh

Comments 46

UFO just landed and posted this here
В монолитах свои проблемы с API. Текущие абстракции (leaky abstractions). Спинлоки, торчащие из интерфейсов, для производительности. Переменные с разделяемым доступом. Неструктурированные, но такие уютные и привычно-домашние интерфейсы, в которых любой посторонний ногу сломит.
Я нежно люблю монолиты за их скорость, но программный интерфейс между подсистемами внутри монолита нельзя называть сильной стороной этого паттерна
UFO just landed and posted this here

Не думали, что придется делать для третьих лиц апишки)

Интересно, не придумал ли кто-то способа энфорсить слабо связанную архитектуру, не заставляя маршалить данные и взаимодействовать по сети?

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

Если ты на одном вычислительном узле и сервисы на одном и том же языке, можно и без сериализации.

Я так понимаю, что в микросервисах огранисения типа агрегатов DDD — нельзя например передавать ссылки, на что-то внутренне а только value object. Пакеты это никак не ограничивают.

Интересно, не придумал ли кто-то способа энфорсить слабо связанную архитектуру, не заставляя маршалить данные и взаимодействовать по сети?

Бить монолит на модули и следить за границами.

нельзя например передавать ссылки, на что-то внутренне а только value object. Пакеты это никак не ограничивают.

Код ревью. Микросервисы слабо связанную архитектуру то не обеспечивают. Это просто более явные границы которые люди всё равно нарушают/проводят неправильно. Чем больше мода на них и проще их становится пилить, тем больше людей появляется у которых всё это дело деплоится как единый связанный монолит.
и следить за границами

следить за соблюдением каких правил?


Код ревью.

А что-то автоматическое?

ИМХО проблемы с RESTAPI: его документирования, консистентности и совместимости это другая плоскость и RESTAPI в микросервисы/монолитах имеют одини и теже проблемы.
Разве что в микросервисной архитектуре добавляется необзодимость собирать документацию из нескольких проектов которые могут быть разработаны на разных языках программирования.

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

Думаю, под «нерешаемой» проблемой скалирования/масштабирования вы имели ввиду «ограниченно решаемую»? Ведь наплодить потоков со своими дубликатами данных в рамках одного адресного пространства — дело несложное, но возникает ограничение размерами физической ноды.
Микросервисы – это «кирпичики», из которых разработчики создают современные приложения.

Как потом деплоится такое приложение?

А то вкупе с многочисленными упоминаниями «общения микросервисов», причём по http, возникает мысль что речь идёт про распределённый монолит взаимодействующий по сети, а не независимые микросервисы.
7 бед — один кубернет. И с ним уже 8 бед.
Но обычно история про deploy отделена от истории про API.
7 бед — один кубернет. И с ним уже 8 бед.

Намекаете на то что мысль выше — верна? Ок, спасибо.

Но обычно история про deploy отделена от истории про API.

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

И утверждения, процитированное в предыдущем комментарии, и:
Де факто есть два способа взаимодействия микросервисов – HTTP Rest и gRPC от компании Google

Показались сомнительными, и как мне показалось, говорят о зависимых друг от друга сервисов(это норма, вопрос лишь в количестве зависимостей).
>>7 бед — один кубернет. И с ним уже 8 бед.
Намекаете на то что мысль выше — верна? Ок, спасибо.

kubernetes как ответ на вопрос «как потом деплоится такое приложение?». под «распределённый монолит взаимодействующий по сети» Вы имеете в виду сильную связность сервисов?

я бы сказал что история про deploy перекликается с историей про обратную совместимость

Безусловно, чтобы обновить API нужно раздеплоить новую версию компонента. Но, Евгений, кажется, я не улавливаю Выше мысль. Развернете?
Вы имеете в виду сильную связность сервисов?

Да. Интересно было бы узнать, независимый ли у микросервисов цикл релизов друг от друга, и сколько сил/времени занимает координация релизов, она ведь всё-равно нужна иногда.

Безусловно, чтобы обновить API нужно раздеплоить новую версию компонента. Но, Евгений, кажется, я не улавливаю Выше мысль. Развернете?

Всё так, наверное не очень корректно было спрашивать именно про деплой в первом коммментарии.
Если происходит поломка обратной совместимости API сервиса, которое используется другими сервисами, нужно править их, а это коммуникации между командами и ожидание обновления от них(=время, деньги, медленнее деливери).

Вы ранее назвали HTTP/gRpc стандартом общения микросервисов, а это значит что общение происходит синхронно(request/response), хотя для поддержания низкой связности всякие event-driven подходы считаются предпочтительнее.

Есть два варианта, как быть с обратной совместимостью апи:


  1. Поддерживать разные версии АПИ в одном сервисе.
  2. Поддерживать в продакшне разные версии самих сервисов, а на апи распределять уровнем выше на входе.

Второй вариант, ИМХО, проще, но может быть чреват проблемами безопасности из-за необновляемости, либо же нужно будет поддерживания несколько версий сразу.


Общение не всегда может быть предпочтимо по событийной модели. В частности, в случае, когда скорость ответа достаточно критична. А иногда и просто логически не имеет смысла. Например, у нас был сервис, который получал какие-то данные из эластика и формировал из них некие структуры в API-ответе, которые надо было потом выдать в конечном итоге на фронтенд. Событийная модель здесь ну прям совсем неуместна.

оба варианта, как мне кажется, имеют одинаковую сложность. Но в одном случае сложность имплементируется в коде, а во втором — в deployment схеме. Помните знаменитую картинку про микросверисы: сложность в коде vs сложность в связях?

Не понял, а что сложного в deployment во втором варианте?

Главным образом, сценарии по апдейту и сценарии нескольких инстансов различных версий работающих с одной базой данных.

База данных — это частный случай, я вообще его не рассматривал. У нас большинство сервисов stateless.

Интересно было бы узнать, независимый ли у микросервисов цикл релизов друг от друга, и сколько сил/времени занимает координация релизов, она ведь всё-равно нужна иногда.


В Acronis сложная модель deploy-я в том смысле, что кроме облака, которым оперируем мы сами, есть еще on-premise deployment, когда наше облако разворачивают customer-ы для своих личных нужд. Поэтому модель «поправили (микро)сервис, залили в прод» у нас не применима. Мы выпускаем целостные релизы Acronis Cyber Cloud где фиксированы версии каждого сервиса
Вы ранее назвали HTTP/gRpc стандартом общения микросервисов, а это значит что общение происходит синхронно(request/response), хотя для поддержания низкой связности всякие event-driven подходы считаются предпочтительнее.


В архитектуре, чисто философски, все компромисс. Иногда ведь и Вы, как архитектор, наверняка отказываетесь от более мощных решений (дающих лучшее масштабирование или производительность) в пользу более понятных решений (дающих более быстрый кодинг, ниже стоимость сопровождения). Cинхронное общение с одной стороны более понятное (простое с точки зрения отладки), с другой стороны более быстрое (все-таки проход через очередь дает много накладных расходов, хотя и добавляет гарантий). Поэтому, как мне кажется, в целом http/gRPC более широко используются чем общение через очереди. Ну и на http можно асинхронное имплементировать (202 и в путь).
Но я согласна с Вами, что event-driven подход имеет свои преимущества
Cинхронное общение с одной стороны более понятное (простое с точки зрения отладки), с другой стороны более быстрое (все-таки проход через очередь дает много накладных расходов, хотя и добавляет гарантий). Поэтому, как мне кажется, в целом http/gRPC более широко используются чем общение через очереди. Ну и на http можно асинхронное имплементировать (202 и в путь).

Так суть как раз в том чтобы не рассматривать http vs queue как две разных реализации транспорта, а делать меньше запросов за данными, получая и сохраняя информацию заранее у себя через всякие Event Notification и Event-Carried State Transfer.

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

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

J2ee, кажется, ограничено Java-ой? А задачи Wamp-а не соответствуют задачам Kubernetes?
В общем, здесь можно идти в долгий философский диспут. Я далеко не фанат kubernetes, но, как Вы правильно сказали, стандарты побеждают в долгосрочной перспективе. А из нескольких альтернативных стандартов выживает не всегда самый лучший с технической точки зрения.

Не возникало ли у Вас в процессе мысли о том что проблем было бы меньше если бы использовалось не RESTAPI а более строгая спецификация? А если RESTAPI, то в ее модификации купно с HATEOAS?

А если RESTAPI, то в ее модификации купно с HATEOAS?

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

Опыта нет. Я бы вообще не применял RESTAPI если бы мобильные разработчики не были сильно против

Не возникало ли у Вас в процессе мысли о том что проблем было бы меньше если бы использовалось не RESTAPI а более строгая спецификация?

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

GraphQL — да получаем документацию тождественную коду по крайней мере по входным и выходным параметрам и гибкость. Отрицательные моменты — обработка ошибок и проблема SELECT N + 1


Старый добрый SOAP — его правильно редко используют так как сложно. Все дело в том что без VisualStudio генерировать сигнатуры сервисов очень сложно — практически невозможно.


Но не только это. Например WAMP-протокол, JSON-RPC, JSON-API и oData (последние два собственно RESTAPI с попыткой внести систематизацию в формирование запросов)

Как решаете проблему с синхронными операциями, если они у вас есть? Или сервисы ведут кросс общение по REST? Спрашиваю, потому что, в нашем случае пришлось уйти на websocket и jsonrpc.

Не кажется ли вам, что микросервисы через REST api жестко связаны? Почему не использованы очереди вроде Rabbit?

На rabbit можно получить ту же связанность, только ещё и не очевидную.

Есть сценарии, в которых используем очереди. Но это глубоко внутренние сценарии. С моей точки зрения, общение по REST проще в сопровождении и быстрее. Кроме того, простота REST для нас критична как для платформенного решения — представьте семинар по пользованию АПИ, где вы объясняете через POSTMAN collection, и семинар, где вы объясните асинхронную модель и работу с очередями. :) Если что-то для внешних пользователей можно упростить, то это всегда нужно упростить.
Общение с внешними пользователями конечно по REST (или другому общепринятому протоколу). Я думал, что REST предлагается и для внутренних взаимодействий сервисов.
В целом внутри тоже REST. Ибо внутренние разработчики — они тоже разработчики. HTTP проще отлаживать, проще кодить. Синхронное взаимодействие в целом легче понимать. Но, конечно, есть обстоятельства, когда гарантии доставки (от использования очередей) оказываются более значимыми чем удобство использования.
Тестирование аннотации против имплементации. Для этого мы генерируем из аннотации (RAML/swagger) клиента, который бомбит сервис запросами. Если ответы будут соответствовать аннотации, а сам сервис не будет падать, значит все хорошо.


Зависит от требований в проекте, но обычно тестирования позитивных сценариев недостаточно.

Остановимся подробнее на тестировании. Подобная полностью автоматическая генерация запросов – сложная задача.


Попробуйте schemathesis. Он генерирует тесткейсы из схемы, проверяет и валидные и невалидные данные, коды возврата вроде тоже проверяет.
проверяет и валидные и невалидные данные

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

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

Тем не менее генерация невалидных данных у нас в плане на ближайшие пару месяцев
Не рассматривали PACT как альтернативу для тестирования АПИ контрактов?
Rust — это хорошая история, но, кажется, тогда всю серверную разработку прийдется переводить с Go на Rust, а это достаточно трудоемко. Вы используете Rust у себя? Можете поделиться опытом?
Sign up to leave a comment.