Комментарии 85
Представьте, что делаете сайт вроде YouTube. Естественно предположить, что на транскодинг файлов будет больше всего ресурсов уходить — логично иметь намного больше инстансов (копий) микросервиса по транскодингу, чем скажем API комментариев. Микросервисы тут идеально вписываются
Но ведь бываюь еще, например, модульные приложения, где тесно связанные компоненты могут быть в одном относительно большом модуле с минимальным количеством внешних связей. При модульном подходе приложение можно не дробить неестественным образом на микросервисы (а потом думать как обратно их заставить работать вместе для какого-нибудь нового отчета). И опыт производителей модульного ПО (SAP, 1C и тысячи других) говорит, что этот подход тоже имеет право на жизнь.
Микросервисам тоже ничто не мешает писать в общую БД, видя только часть ее структуры через запросы.
Ребят, а зачем вы опять обсуждаете "что лучше для ремонта: молоток или пассатижи"?
Я часто использую микросервисы, я часто пишу монолиты, я часто пишу модульные сервисы, а также отдельные скрипты, иногда на сервисной шине, иногда без.
Я к тому, что слишком много пошло статей, постулирующих что-то как панацею. А не как один инструмент из многих.
И при этом все избегают писать о самом ответственном. О критериях принятия решений на предмет какой из инструментов выбрать и почему именно в конкретной ситуации.
Весь startup-мир глумится над PHP, jQuery и MySQL, а один из самых успешных проектов десятилетия Slack сделан на них :)
PS. Сложно себе представляю реализацию большого проекта на jQuery, но факт есть факт
У нас сейчас несколько микросервисов используют одну общую базу (но записи из неё маппятся по разному и по набору полей, и по иерархии объектов), несколько свою собственную, а у одного — три базы. Это не считая чего-то типа ДВХ, которые собирает данные из всех имеющихся баз в одну для аналитики.
Хорошая иллюстрация того, что если проект грамотно организован, то постепенное расщепление монолита на микросервисы это не подвиг, а рутина. И не нужно сразу кидаться весь проект пропускать через мясорубку, а можно делать это постепенно, по мере выделения изолированного функционала и данных.
ИМХО, лучше бы вот этому статью посвятили — как в боевых условиях проводить постепенный рефакторинг действующего монолита в микросервисы. Это принесло бы намного больше пользы, чем всем уже надоевшие «микросервисы для чайников».
2) Возможность постепенного перехода является одним из плюсов этой идеи :)
3) Судя по комментариям, очень многие несогласны, что сама идея микросервисов хорошая. Не рано ли им предлагать переход? :) Создается впечатление, что людям пока надо слышать доводы в пользу. Даже если взять глобальную сцену IT, крупных проектов, которые приняли парадигму – намного менее половины (если судить по публичным источникам)
Джуниоры кидаются везде применять новую освоенную ими технологию на волне эмоций под эндорфинами от ощущения собственной крутости, и всем впаривают что вот она — истина, совесть и честь эпохи. Ваша статья выглядит точно так же, разумеется у всех кто перерос этап джуна она вызывает скепсис.
Потому что мидлу уже не особенно интересна абстрактная крутость — у него в голове применение технологии уже привязано к очереди и приоритетам задач. И он ищет эффективные решения руководствуясь их практической применимостью и сроками.
А сеньор или архитектор в довесок рассматривают последующее сопровождение и риски для продакшена, особенно на хайлоаде.
В условиях стремительно меняющихся условий, целей и задач, людям нужно нечто большее, чем «introduction into...» чтобы воспринять новую технологию. Им нужны кейсы.
Нужны статьи не «Как я полюбил микросервисы, потому что {{ reason }}», а >>> «Разрываем сансару рефакторинга, выделяя в микросервис модуль, который вы итак переписываете на 50%-80% каждую неделю» или «Как разделить ваш центр управления колонизацией марса на микросервисы, и начать спать».
Микросервисы, как архитектурный паттерн, позволяют снизить стоимость (издержки от) неоптимального исходного кода, ускорить разработку и повысить качество сна.
Вот только почему-то авторы в расчёт не берут время/ресурсы потраченные на создание всех этих маршрутов, связей между сервисами. Что это будет? Просто http запросы? Синхрониться через базу? А может брокер типо RabbitMQ?
А как они искать друг друга будут? Какая-то сервис дискавери тулза? Как по мне, правильно построить архитектуру на микросервисах не так-то просто. И если у вас были проблемы с монолитом (почему всегда сравнивают с ним?!), то просто используя микросервисы вы от спагетти кода не уйдёте, так как проблема в головах.
Даже если код одинаково плохим остается — когда он порезан на куски, с ним легче справляться и его проще рефакторить
А временные затраты на общение сервисов не такое большое — все библиотеки и провайдеры предоставляют SDK. Во многих фреймворках с ходу включены клиенты вроде AWS SQS или IronMQ
Ну, взять вот даже эту картинку — одна какашка и 5 какашек. Что легче зарефакторить? Что будет меньше потенциальных сбоев во время рефакторинга давать? (деплои 1/5 системы или каждый раз деплой всей системы)Потенциальных сбоев в микросервисах куда больше становится. Если взять приведённый вами монолит, то там проблема будет в каком-то модуле (мы же пишем модульно, да?). В микросервисах может фаервол затупить, сервис дискавери тулза откажет, подвиснут соединения не пойми почему. То есть, тут ОГРОМНАЯ проблема помимо кодинга ещё и в плане администрирования всего этого.
Даже если код одинаково плохим остается — когда он порезан на куски, с ним легче справляться и его проще рефакторитьЛегче справится с модульным приложением, а не с микросервисами, например.
А временные затраты на общение сервисов не такое большое — все библиотеки и провайдеры предоставляют SDK. Во многих фреймворках с ходу включены клиенты вроде AWS SQS или IronMQЯ вот смотрю пинги между нашими сервами в приватной сети. Он может до 1мс доходить. В случае с http — там ещё куча мусорных хедеров будет.
В случае с RabbitMQ каким-нить, вам нужен будет кластер (нам же нужна надёжная система). Вот только, что вы будете делать при большой нагрузке, когда кластер откажет? Пойдёте пробовать кафку? Хорошо, если у вас есть тот, кто имеет опыт с такими монстрами, а если нет?
— Можно полностью положится на облачных провайдеров, очереди и сообщения обычно стоят очень недорого.
Потенциальных сбоев в микросервисах куда больше становится. Если взять приведённый вами монолит, то там проблема будет в каком-то модуле (мы же пишем модульно, да?).
Теоретически же, какяпонимаю.
А в реале — может быть что угодно.
Микросервисы ставят «более высокие перегородки» между модулями, кмк.
Плюс, если задача легко подвергается декомпозиции — наличие большого числа микросервисов может быть выгодно с точки зрения реконфигурирования и подгонки конфигурации под текущие потребности.
То есть, тут ОГРОМНАЯ проблема помимо кодинга ещё и в плане администрирования всего этого.
Девопсы тоже хотят кушать :)
Микросервисы, как архитектурный паттерн, позволяют снизить стоимость (издержки от) неоптимального исходного кода, ускорить разработку и повысить качество сна.
В обмен на усложнение развертывания, сложности с управлением версиями и вообще резкое уменьшение количества сна у Ops.
2) Время ops зачастую дешевле времени разработчиков стоит, да и ops обычно меньше в штате :)
3) А какие сложности с управлением версиями?
Развертывание один раз настраивается и редко меняется, обычно.
"Обычно". То-то я регулярно что-нибудь перенастраиваю и переразворачиваю.
Зато развертывание изолированное — по частям, что безопаснее (вся система не рухнет из-за одного элемента)
… это если у вас так сделано, что микросервисы переживают отсутствие зависимостей. Что, в общем случае, далеко не всегда правда. А еще можно положить среду взаимодействия — и упадет все.
Время ops зачастую дешевле времени разработчиков стоит, да и ops обычно меньше в штате
Это пока ops не начинают будить разработчиков вопросами "тут твоя [...] упала, что делать-то?"
А какие сложности с управлением версиями?
Ну как же, есть у вас микросервис D, от которого зависят микросервисы A и B. Вы разрабатываете микросервис A, и вас не устраивает функциональность D. Вы ее дописываете. Разворачиваете D — и внезапно, B падает, потому что вы чего-то не учли.
(вот вам и "безопасное изолированное развертывание": развернули один компонент — уронили все его зависимости)
Понятно, что в этом месте люди подпрыгивают и кричат "тесты! тесты!". Но, положа руку на сердце, если люди не могут написать тесты на монолитное приложение, то почему мы думаем, что они будут их писать на микросервисы?
То-то я регулярно что-нибудь перенастраиваю и переразворачиваю.
Это называется «работа». За неё опсам платят деньги.
Это пока ops не начинают будить разработчиков вопросами «тут твоя [...] упала, что делать-то?»
О, видите, даже в этой истории у разработчика всё-таки было время поспать. Что делать? Глупый вопрос — читать логи, конечно.
Вся суть микросервисов в том, что часть работы можно эффективно переложить с плеч разработчиков на на девопсов, ещё часть — эффективно разделить между разработчиками (каждому по микросервису в зубы). В случае монолита — этот монолит лежит камнем на плечах (в лучшем случае у каждого, в худшем — у сильнейшего разработчика).
Я с большим интересом слежу за темой микросервисов, но проанализировав проекты которые веду пришел к выводу, что издержки будут выше чем потенциальный профит.
Вот если нужно что-то сбоку к большому приложению прикрутить ИМХО микросервисы тут отличный выход.
В целом хотелось бы узнать об узких местах микросервисного подхода и его подводных камнях. Я как миним вижу потенциальные трудности в согласование команд работащиюми над разными микросервисами и возможные проблемы на каналах связи. Хочется взвешенного подхода, который уделяет слабым сторонам не меньше внимания чем сильным.
Самим командам работать станет проще — с их точки зрения достаточно написать небольшое приложение, удовлетворяющее спецификациям API.
В принципе, все паттерны разработки, все «крутые» фишки ООП или функционального программирования — это всегда улучшение эффективности разработки ценой усложнения системы.
Можно написать фронт-енд на jQuery в 1000 строк, можно на Angular в 50. Второе эффективнее и проще обновляется в будущем, но требует изучения фреймворка, понимания хороших практик программирования.
Самим командам работать станет проще — с их точки зрения достаточно написать небольшое приложение, удовлетворяющее спецификациям API.
Ага, это если спецификации есть и не меняются. А они всегда меняются, и дальше начинается цирк с версионированием.
Это называется «работа». За неё опсам платят деньги.
Разработчикам тоже за работу платят деньги. Внезапно.
Вся суть микросервисов в том, что часть работы можно эффективно переложить с плеч разработчиков на на девопсов, ещё часть — эффективно разделить между разработчиками (каждому по микросервису в зубы).
В случае монолита это тоже возможно (я прямо сейчас на это смотрю). А самое главное, микросервисы не дадут эти преимущества сами по себе, для этого еще нужны девопсы (которых надо взять), эффективное разделение и все остальное.
Дело же не в том, что микросервисы плохи, дело в том, что они не бесплатны.
Как обычно, просто оставлю это здесь. Читайте оригинал.
http://microservices.io/patterns/index.html
А для перевода требуется еще и родным языком владеть на уровне несколько выше среднего.
Судя по половине переводов последнего времени на хабре, нужно владеть ссылкой на translate.google.com или копией Промпта. И старательно избегать спеллчекера.
Когда вы пишете программу из 100 строк, то, в принципе, не имеет значения насколько крутой вы выбрали дизайн или насколько четко вы будете следуете модным идеям.
1. Следование модным идеям — разве залог успеха? :)
От этих модных идей программа распухнет со 100 строк до 200. :)
2. Я хз, но читать и разбираться в 100 строках говнокода — это разбираться в говнокоде.
Вероятно, вы начинаете делить исходные файлы на модели, контроллеры и так далее.
и ранее
А когда файлов стало много, вы начинаете организовывать их в папки.
То есть сначала файлы группировали от балды по папкам? :)
Фреймворки зачастую насаждают свои конвенции (правила), систематические разбиение приложения на файлы и каталоги.
Так мы уже ж разбили сами все, как следует:
Вероятно, вы начинаете делить исходные файлы на модели, контроллеры и так далее.
Это дополнительная работа, барьер, который разработчики не будут преодолевать без абсолютной необходимости.
А перед этим в монолите они без надобности вызывали что-то? :)
Так как отдельно взятый микросервис – это не более, чем класс или два, вырезанных из вашего монолитного приложения, то теперь у вас меньше, значительно меньше файлов в рамках одного открытого приложения в IDE.
Шта?
У меня 200 классов.
199 классов и 1 микросервис вряд ли чем-то помогут.
Ну и 200 микросервисов — вряд ли что-то хорошее. :)
Если у вас миллион пользователей, то перевести 200 классов в 200 сервисов — может быть вполне разумной идеей.
До перехода, в независимости от нагрузки, каждому классу «нужны» будут остальные 199 — остальные 199 деплоятся вместе и находятся в ОЗУ вместе. После перехода на микросервисы, у 2 классов может быть по 100 «инстансов», а у остальных 198 всего по одному (потому что они проще).
Так как отдельно взятый микросервис – это не более, чем класс или два, вырезанных из вашего монолитного приложения
Класс или два обычно нет смысла выделять в микросервис, если этого не требуют админы/пользователи. «Микро» в «микросервис» значит, имхо, не мало кода в сервисе, а мало ответственностей перед клиентом у сервиса, тот же SRP из SOLID, вынесенный на уровень приложения (как относительно независимой программы). А проще говоря unix-way.
то теперь у вас меньше, значительно меньше файлов в рамках одного открытого приложения в IDE.
А зато открыто несколько проектов без навигации, автодополнения и, главное, синхронизации между ними :) Чтобы развернуть дев-среду надо поднимать локально не одно приложение (и среды для него), а несколько.
В общем, использовать микросервисы ради моды и(или) ради уменьшения возможности напартачить разработчикам — стрелять себе в ноги. Звоночки о необходимости что-то выделять в микросервис должны идти прежде всего от эксплуатации/пользователей, а не от разработчиков. И то, зачастую проблемы эксплуатации решаются распределенными монолитами — ставим новые инстансы монолитов (у вас же монолит масштабируется горизонтально? он же стейтлесс?) пока эксплуатация или руководство не взвоёт, что ресурсы расходуются не оптимально, что ради одного модуля, который нужно масштабировать горизонтально, нужно разворачивать приложение полностью.
Вообще, если хочется попробовать микросервисы с прицелом на горизонтальное масштабирование, сначала начните с горизонтального масштабирования стандартной инфраструктуры (она, обычно, является готовым микросервисом, с одной-двумя ответственностями), потом самого монолита, почувствуйте прелесть распределенных систем, если максимальный опыт в них у вас заключается в вынесении СУБД на другой хост, а потом уже думайте надо ли оно вам.
а разработчики там вредные и они не хотят добавлять ненужный (с их точки зрения) маршрут лишь ради того, чтобы сделать вашу жизнь проще.Так и скажу начальнику на вопрос: «Когда будет готова новая фича?»
Но через что будет проходить связь микросервисов — по tcp/ip стеку (http запросы)?
И как при этом организовывать протоколы передачи данных между мСервисами?
Как между ними согласовывать работу (какой-то диспетчер)?
Какие параметры передавать и по какому адресу?
2) Как правило создается некий реестр сервисов по типу, из которого можно всегда адрес живого инстанса (экземпляра) сервиса взять. Популярное бесплатное ПО — consul.
В Docker Cloud создается внутренняя сеть автоматически между всеми сервисами, достаточно друг друга по хостнейму стучать.
В AWS можно на мониторинг и Route53 достаточно просто привязать
Диспетчеризация сообщений и ответов на них — архитектурное решение конкретного проекта. Могут быть исключительно прямые вызовы, может быть, навскидку, какая-то шина, очередь сообщений, расшаренное хранилище типа СУБД.
Адреса определяются какими-то service discovery или просто хардкодятся в конфигах клиентов DNS-имена, а на DNS прописываются серверы.
А контракты, через которые происходит общение между сервисами, например, те же классы-сообщения, ходящие в RabbitMQ?
По опыту живого проекта, общий код выделяется в библиотеки которые потом доставляются менеджером зависимостей (например composer для php, или glide для golang). Зависимости актуализируются согласно семантическому версированию.
Что касается контрактов, для REST есть спецификация описания интерфейсов swagger, из неё можно сгенерировать клиентскую библиотеку (методы и структуры) и внедрить её как зависимость.
В общем случае (rabbitmq, protobuf) протокол взаимодействия (методы и структуры) должен быть описан в клиентской библиотеке.
У меня каждый микросервис экспортирует документацию в формате Swagger (как и у vearutop), а данные которые полезны более, чем одному сервису транслируются в топиках SNS в формате JSON. Нет никакой сериализации объектов, так как используются разные языки и фреймворки.
Как сделать контракт на эти JSON сообщения — наверное, можно документировать формат + покрыть тестами в сервисах, которые их публикуют. Можно, наверное, и обертку для каждого языка реализовать с поддержкой версий
Если на разных, то приходится дублировать структуры/логику, но лучше выделить или перенести логику в один микросервис, дублируя только его интерфейсные структуры данных. Впрочем и если одинаковый, то вместо либы с логикой можно сделать микросервис, а в либе оставить только структуры данных типа dto.
Городить микросервисы имеет смысл в условиях неразберихи и переизбытка разработчиков. Таким образом можно явно распределить ответственность между командами и улучшить диагностируемость системы (мониторинг компонентов, хелсчеки).
Крайне важным при этом становится соблюдение контрактов и поддержка обратной совместимости (с версированием интерфейсов).
Процесс миграции и разделения монолита может занять годы.
Что касается масштабирования, хороший монолит не слишком уступает тут сервис-ориентированной архитектуре. Как правило все упирается в централизованные хранилища и диспетчеры.
Действительно полезное свойство — параллельные композиционные релизы, когда из-за ошибки в одной задаче, не приходится перестраивать весь реализ.
Не стоит рассматривать СОА как способ оптимизации производительности, межкомпонентные задержки и блокировки скорее всего ухудшат общую скорость.
Отдельной головной болью может стать обеспечение транзакционности в рамках всего приложения, а также трассировка кто кого куда вызвал при дебаге.
СОА может повысить производительность для проекта с очень несимметричной нагрузкой вроде YouTube, где сервисов по транскодингу надо в сотни раз больше, чем сервисов по, скажем, публикации комментариев.
Так как шлюз API может асинхронно выполнять запросы к разным сервисам — здесь тоже можно выиграть в производительности, в зависимости от проекта.
Все-таки уступает по себестоимости и эффективности. Масштабируя монолит, ты масштабируешь _все_ элементы приложения, а масштабируя микросервисы — только те, которым сейчас нужно больше ресурсов. А раз мы поднимаем «машину» по-меньше — то снизятся расходы на облачный хостинг, да и будет очевиднее, что нуждается в оптимизации, а что нет.
Основная суть микросервисов — это масштабирование на бизнес-уровне.
Пусть в компании X определена общая политика — все микросервисы должны предоставлять REST API. Вот книжка с good practice, следуйте.
Теперь каждая команда обозначает API через какой-нибудь swagger/graphql/whatever и имплементирует уже как хочет. Полная независимость разработки между командами, никаких баундов. Технологии и языки вольна выбирать команда, ответственность за продукт end to end, бизнес-домен как зона ответственности команды и продукта.
Единственное правило — не ломать API. А дальше уже скейлите, заливайте огрехи баблом и т.п. Когда у вас на фронте все рендерится по несколько сотен мс, не пофиг ли, что там на бэкэнде теперь оверхед на запросы между сервисами?
Теперь повторите такой же трюк с монолитом на масштабах больше 1к разработчиков. Чтоб гибко и независимо. Вот почему это работает для таких гигантов как Netflix или Spotify
Для команд по 5-6 человек на всю компанию вы на нормальный девопс убьете не меньше ресурсов, чем на, собственно, разработку микрух. Это из собственного опыта.
Хотя сейчас kubernetes + AWS сервисы и облегают задачу с service discovery, failover и т.п., но мороки все равно хватает.
Я пользуюсь Docker Cloud, он очень много сам за меня делает «behind the scenes». Но мне немного повезло — я сетевой инженер в прошлом, поэтому нам не пришлось нанимать devops вообще.
На _нормальный_ девопс вороха микрух времени уйдет не меньше, чем на разработку. Для небольших команд это может быть критично, т.к. вместо имплементации фич они будут дрочиться, почему же у них на EC2 инстанс не качается докер имадж из приватного ECR. А выделять на это приходится человека, который код пишет.
У меня есть опыт работы и в небольшой команде, и в крупной компании. Есть возможность сравнить плюс и минусы подходов на практике, без теоретического «ну оно там чото скейлится, чтоб обрабатывать мои 100 запросов в час».
Микросервисы подымают ошибки на новый уровень — на интеграционный. То, что раньше было проблемой в «монолите» на стыке абстракций, теперь находится на стыке абстракций в двух или более микросервисах, которые могут поддерживаться разными командами (код везде идеальный, а интеграция будет содержать не тривиальную ошибку).
Перед выбором микросервисов стоит четко понимать цели, сложности и возможные последствия этого выбора.
Если нужно сделать что-то хорошее и производительное — это «монолит».
Если нужно сделать что-то сложное, но масштабируемое — это микросервисы.
ps: Судя по прикрепленным изображениям Вы заменили спагетти-код на спагетти-микросервисы.
Про спагетти, вероятно, неудачные картинки. В Интернете ходит популярная картинка, где микросервисы с равиоли сравнивают (спагетти — плохой код 90-ых, лазанья — слои в MVC/монолитах, равиоли — микросервисы)
Как я научился не волноваться и полюбил микросервисы, часть 1: Эффекты плохого кода