Преамбула
В этой серии постов я собираюсь обсудить различные преимущества микросервисов как архитектурного паттерна. Я постараюсь охватить аспекты, которые либо не обсуждаются в сети совсем или обсуждаются, но недостаточно глубоко.
Все паттерны разработки, как правило, имеют свои плюсы и минусы. Таким образом, преимущества зачастую носят субъективный характер и разработчику следует выбирать подходящие модели в зависимости от условий и контекста.
Я постараюсь выделить объективные или почти-объективные плюсы — преимущества, которые дадут положительный эффект любому разработчику независимо от языка программирования, размера команды или диеты. Первая статья посвящена стоимости плохо написанного кода – мы сравним эффекты влияния такого кода на монолитные приложения и на микросервисы.
Кроме того, позже, когда наберется несколько статей, я планирую смонтировать видео для YouTube – анимации и скринкасты донесут мою точку зрения яснее.
Введение: Как конвенции и фреймворки улучшают запутанный код
Когда вы пишете программу из 100 строк, то, в принципе, не имеет значения насколько крутой вы выбрали дизайн или насколько четко вы будете следуете модным идеям. Небольшое приложение относительно легко понять, просто потому что оно небольшое. А начать его оптимизировать было бы анти-паттерном само по себе – такую оптимизацию часто называют преждевременной.
Как правило, вы начинаете рефакторить код, когда заметили, как выросли исходные файлы или как вы повторяете одну и ту же логику более двух раз. Но если у приложения всего 100 строк, сколько реально таких повторений может поместиться?
И тут ваш начальник просит все больше и больше фич, и ваше приложение начинает расти. Теперь уже не имеет смысла держать всю логику в одном файле, необходимо разбить приложение на несколько файлов. А когда файлов стало много, вы начинаете организовывать их в папки. Когда стало много и файлов и папок, вы ищете способ логически сортировать элементы. Вероятно, вы начинаете делить исходные файлы на модели, контроллеры и так далее.
Начинает появляться опасность запутанного кода – файл X обращается файлу Y, Y зависит частично от файла Z, файл Z импортирует несколько функций из файлов А и Б. Приходит младший разработчик, изменяет один файл и ломается всё приложение, а вы должны исправить ошибку, прежде чем боссу позвонит первый разгневанный клиент.
Решать проблему к нам пришли фреймворки. Фреймворки зачастую насаждают свои конвенции (правила), систематические разбиение приложения на файлы и каталоги. Например, контроллеры должны иметь дело только с веб-запросами, классы моделей должны содержать бизнес-логику, шаблоны должны содержать только HTML, конфигурация должна храниться в отдельном месте и желательно зависеть от текущей среды.
Хотите добавить ресурс? Измените файл маршрутов!
Хотите добавить дополнительную логику, которая запускается после сохранения записи? Подпишитесь на событие!
Хотите добавить дополнительные условия для запросов в базу данных? Создайте класс поведения таблицы!
И так далее.
Это не просто помогает разрабатывать более крупные монолитные приложения, это единственный способ разработать крупное приложение и не сойти с ума! Если каждый член команды будет иметь право добавлять код куда угодно, то ваш процесс разработки выскочит из-под контроля довольно быстро.
Посмотрим на структуру пустого приложения на Ruby on Rails:
./app/assets
./app/assets/config
./app/assets/images
./app/assets/javascripts
./app/assets/javascripts/channels
./app/assets/stylesheets
./app/channels/application_cable
./app/controllers
./app/controllers/concerns
./app/helpers
./app/jobs
./app/mailers
./app/models
./app/models/concerns
./app/views/layouts
./bin
./config
./config/environments
./config/initializers
./config/locales
./db
./lib
./lib/assets
./lib/tasks
./log
./public
./test
./test/controllers
./test/fixtures
./test/fixtures/files
./test/helpers
./test/integration
./test/mailers
./test/models
./tmp
./vendor
./vendor/assets
./vendor/assets/javascripts
./vendor/assets/stylesheets
Реальность такова, что в вашей команде могут быть неопытные (или с похмельем) коллеги, которые будут нарушать вроде бы простые конвенции время от времени, и код будет становится всё сложнее и уродливее, несмотря на выбор «правильных» фреймворка и структуры.
Написание хорошего кода особенно важно в контексте большого приложения. Принципы SOLID, паттерны разработки, фреймворки и конвенции появились в первую очередь, чтобы помочь разработчику с крупными приложениями.
Монолиты и микросервисы: Влияние на спагетти-код
Вот что делают микросервисы со спагетти:
Ой, не та картинка. Вот так примерно выглядит спагетти-код в случае монолитного приложения:
Один класс (или функция) вызывает другой класс, а тот класс зависит от другого класса, а тот класс зависит от нескольких других классов и так далее. Вы хотите пробежаться по коду и исправить ошибку — будьте готовы открыть много файлов и изучать их параллельно. Почему я считаю, что это является характеристикой именно монолитного приложения?
В монолитном приложении, модули (элементы) кода вызывают друг друга непосредственно –используя тот факт, что все они загружены в оперативную память в рамках одной машины. Это приводит к тому, что ленивые разработчики излишне тесно связывают части приложения между собой.
Микросервисы, с другой стороны, как правило, общаются через веб-запросы (обычно RESTful) или через сообщения. Таким образом, разработчик не может просто взять и вызвать функционал одного микросервиса из другого микросерфиса – как минимум, придется создать дополнительный маршрут, открыть этот метод в контроллере и так далее. Это дополнительная работа, барьер, который разработчики не будут преодолевать без абсолютной необходимости. Существует также вероятность того, что тот другой микросервис в настоящее время поддерживается другой командой вашей компании, а разработчики там вредные и они не хотят добавлять ненужный (с их точки зрения) маршрут лишь ради того, чтобы сделать вашу жизнь проще.
В результате, вы, скорее всего, будете иметь меньшее количество по-лучше организованных соединений. А факт меньшего количества зависимостей между микросервисами (различными функциональными модулями вашего приложения) означает, что если микросервис А плохо реализован, он не будет иметь такого дурного влияния на другие микросервисы.
В среде микросервисов плохой код изолирован.
Микросервисы: Успокой своего внутреннего перфекциониста
Так как отдельно взятый микросервис – это не более, чем класс или два, вырезанных из вашего монолитного приложения, то теперь у вас меньше, значительно меньше файлов в рамках одного открытого приложения в IDE. Таким образом, вероятно, уже не нужно так сильно заботиться о строгих правилах наименования файлов и папок и, возможно, вы даже можете ослабить правила SOLID.
Теперь вы можете позволить себе «халяву» – можно сделать несколько ошибок, несколько неоптимальных реализаций, а ваше приложение всё еще достаточно легко читать и править, потому что ваш LoC (Lines of code) упал с нескольких тысяч до жалких десятков или сотен!
Процесс разработки ускорится, потому что вы меньше обеспокоены строгими правилами и мириадой зависимостей!
Вывод
Микросервисы, как архитектурный паттерн, позволяют снизить стоимость (издержки от) неоптимального исходного кода, ускорить разработку и повысить качество сна.
Комментарии (83)
amaksr
27.12.2016 09:20+2Почему в статьях про микросервисы всегда идет сравнение с монолитом? Что микросервис, что монолит — две противоположные крайности, которые имеет смысл использовать лишь иногда.
Но ведь бываюь еще, например, модульные приложения, где тесно связанные компоненты могут быть в одном относительно большом модуле с минимальным количеством внешних связей. При модульном подходе приложение можно не дробить неестественным образом на микросервисы (а потом думать как обратно их заставить работать вместе для какого-нибудь нового отчета). И опыт производителей модульного ПО (SAP, 1C и тысячи других) говорит, что этот подход тоже имеет право на жизнь.dusterio
27.12.2016 09:36Модульные приложения в рамках конкретной этой статьи — вероятно да, не так сильно и отличаются от микросервисов. Но по факторам из последующих статей — разница большая (отдельный деплой, возможность разделения на уровне сети, масштабирование только нагруженных элементов и тд)
yvm
28.12.2016 12:16Мало очевидный аспект, но в модульном приложении легко допустить связанность через БД
Toshiro
28.12.2016 12:35Микросервисам тоже ничто не мешает писать в общую БД, видя только часть ее структуры через запросы.
Ребят, а зачем вы опять обсуждаете "что лучше для ремонта: молоток или пассатижи"?
dusterio
28.12.2016 14:17У каждого микросервиса должно быть отдельное хранилище, это как раз одно из преимуществ архитектуры (можно под конкретные данные выбрать конкретный вид sql/nosql базы)
Toshiro
28.12.2016 14:26+1Должно быть != Единственно верный способ.
Я часто использую микросервисы, я часто пишу монолиты, я часто пишу модульные сервисы, а также отдельные скрипты, иногда на сервисной шине, иногда без.
Я к тому, что слишком много пошло статей, постулирующих что-то как панацею. А не как один инструмент из многих.
И при этом все избегают писать о самом ответственном. О критериях принятия решений на предмет какой из инструментов выбрать и почему именно в конкретной ситуации.dusterio
28.12.2016 15:53В конце концов, наверное, все зависит от качества исполнения, больше чем от выбранных инструментов.
Весь startup-мир глумится над PHP, jQuery и MySQL, а один из самых успешных проектов десятилетия Slack сделан на них :)
PS. Сложно себе представляю реализацию большого проекта на jQuery, но факт есть факт
yvm
29.12.2016 16:53Таки единственно верный способ.
Toshiro
29.12.2016 19:25Никогда не видели больших систем, где на уровне СУБД есть свой отдельный слой логики, который работает независимо от логики уровня приложений? У многих сервисов в таких системах вообще нет своего хранилища, они выступают лишь как обработчики. Вы, конечно, можете написать отдельный сервис который бы обслуживал для них операции с частями/БД. Или каждому из них написать парный микро/сервис, для работы с частями/БД. А можете просто в нужных местах обращаться к конкретным хранимым процедурам и вьюхам, создаваемым для использования N-микро/сервисами. Есть множество способов реализации и нет единственно верного, есть наиболее подходящий к задаче.
VolCh
28.12.2016 15:43Если рассматривать глобально, то отдельное хранилище (в том числе СУБД) — это тоже микросервис по сути, просто уже готовый сторонний. Никто нам не запрещает сделать зависимым от одного микросервиса несколько других ни в случае, если все они нашей разработки, ни в случае если часть из них готовые сторонние.
У нас сейчас несколько микросервисов используют одну общую базу (но записи из неё маппятся по разному и по набору полей, и по иерархии объектов), несколько свою собственную, а у одного — три базы. Это не считая чего-то типа ДВХ, которые собирает данные из всех имеющихся баз в одну для аналитики.dusterio
28.12.2016 15:56А если один микросервис решил мигрировать схему БД немного? Если вам из-за этого придется что-то менять в остальных – это, вроде как, нарушает всю идею и лишает плюсов
Toshiro
28.12.2016 16:11Наоборот, это как раз одно из преимуществ микросервисов, о которых вы рассуждаете. В случае изменений в каком-то микросервисе, вы можете быстро создать новый, реализующий новый функционал (например вынести часть БД отдельно, раз уж это точно потребовалось), а потом не останавливая всей работы переключить систему на него, а старый просто вывести из работы.
Хорошая иллюстрация того, что если проект грамотно организован, то постепенное расщепление монолита на микросервисы это не подвиг, а рутина. И не нужно сразу кидаться весь проект пропускать через мясорубку, а можно делать это постепенно, по мере выделения изолированного функционала и данных.
ИМХО, лучше бы вот этому статью посвятили — как в боевых условиях проводить постепенный рефакторинг действующего монолита в микросервисы. Это принесло бы намного больше пользы, чем всем уже надоевшие «микросервисы для чайников».dusterio
28.12.2016 16:181) Если речь идет о новой реализации существующего функционала, то конечно!
2) Возможность постепенного перехода является одним из плюсов этой идеи :)
3) Судя по комментариям, очень многие несогласны, что сама идея микросервисов хорошая. Не рано ли им предлагать переход? :) Создается впечатление, что людям пока надо слышать доводы в пользу. Даже если взять глобальную сцену IT, крупных проектов, которые приняли парадигму – намного менее половины (если судить по публичным источникам)Toshiro
28.12.2016 17:12+2Все верно, людям нужны доводы в пользу. Причем не рафинированные вроде «смотрите какая крутая технология», а на конкретных примерах. Желательно вызывающих боль и страдания на реальном продакшене.
Джуниоры кидаются везде применять новую освоенную ими технологию на волне эмоций под эндорфинами от ощущения собственной крутости, и всем впаривают что вот она — истина, совесть и честь эпохи. Ваша статья выглядит точно так же, разумеется у всех кто перерос этап джуна она вызывает скепсис.
Потому что мидлу уже не особенно интересна абстрактная крутость — у него в голове применение технологии уже привязано к очереди и приоритетам задач. И он ищет эффективные решения руководствуясь их практической применимостью и сроками.
А сеньор или архитектор в довесок рассматривают последующее сопровождение и риски для продакшена, особенно на хайлоаде.
В условиях стремительно меняющихся условий, целей и задач, людям нужно нечто большее, чем «introduction into...» чтобы воспринять новую технологию. Им нужны кейсы.
Нужны статьи не «Как я полюбил микросервисы, потому что {{ reason }}», а >>> «Разрываем сансару рефакторинга, выделяя в микросервис модуль, который вы итак переписываете на 50%-80% каждую неделю» или «Как разделить ваш центр управления колонизацией марса на микросервисы, и начать спать».dusterio
29.12.2016 03:43Целиком с Вами согласен, и учту замечания при написании второй части!
Привести практические примеры, в принципе, не проблема. Просто не подумал! :)
VolCh
28.12.2016 18:38+1Миграция схемы БД — это, по сути, изменение интерфейса «микросервиса» СУБД. Во-первых, мигрировать можно обратно совместимым способом, во-вторых, основной плюс, имхо, это возможность масштабирования одного модуля большой системы, а не только всех одновременно.
Toshiro
29.12.2016 11:11В третьих, никто не отменял уровни абстракции: метабаза в несколько слоев, если потребуется, и вьюхи. Все зависит от конкретных обстоятельств.
Suvitruf
27.12.2016 11:44+10Почти каждая статья про микросервисы заканчивается:
Микросервисы, как архитектурный паттерн, позволяют снизить стоимость (издержки от) неоптимального исходного кода, ускорить разработку и повысить качество сна.
Вот только почему-то авторы в расчёт не берут время/ресурсы потраченные на создание всех этих маршрутов, связей между сервисами. Что это будет? Просто http запросы? Синхрониться через базу? А может брокер типо RabbitMQ?
А как они искать друг друга будут? Какая-то сервис дискавери тулза? Как по мне, правильно построить архитектуру на микросервисах не так-то просто. И если у вас были проблемы с монолитом (почему всегда сравнивают с ним?!), то просто используя микросервисы вы от спагетти кода не уйдёте, так как проблема в головах.
dusterio
27.12.2016 11:53-1Ну, взять вот даже эту картинку — одна какашка и 5 какашек. Что легче зарефакторить? Что будет меньше потенциальных сбоев во время рефакторинга давать? (деплои 1/5 системы или каждый раз деплой всей системы)
Даже если код одинаково плохим остается — когда он порезан на куски, с ним легче справляться и его проще рефакторить
А временные затраты на общение сервисов не такое большое — все библиотеки и провайдеры предоставляют SDK. Во многих фреймворках с ходу включены клиенты вроде AWS SQS или IronMQSuvitruf
27.12.2016 12:38+7Ну, взять вот даже эту картинку — одна какашка и 5 какашек. Что легче зарефакторить? Что будет меньше потенциальных сбоев во время рефакторинга давать? (деплои 1/5 системы или каждый раз деплой всей системы)
Потенциальных сбоев в микросервисах куда больше становится. Если взять приведённый вами монолит, то там проблема будет в каком-то модуле (мы же пишем модульно, да?). В микросервисах может фаервол затупить, сервис дискавери тулза откажет, подвиснут соединения не пойми почему. То есть, тут ОГРОМНАЯ проблема помимо кодинга ещё и в плане администрирования всего этого.
Даже если код одинаково плохим остается — когда он порезан на куски, с ним легче справляться и его проще рефакторить
Легче справится с модульным приложением, а не с микросервисами, например.
А временные затраты на общение сервисов не такое большое — все библиотеки и провайдеры предоставляют SDK. Во многих фреймворках с ходу включены клиенты вроде AWS SQS или IronMQ
Я вот смотрю пинги между нашими сервами в приватной сети. Он может до 1мс доходить. В случае с http — там ещё куча мусорных хедеров будет.
В случае с RabbitMQ каким-нить, вам нужен будет кластер (нам же нужна надёжная система). Вот только, что вы будете делать при большой нагрузке, когда кластер откажет? Пойдёте пробовать кафку? Хорошо, если у вас есть тот, кто имеет опыт с такими монстрами, а если нет?dusterio
28.12.2016 12:23-2— 1мс — это очень даже низкая задержка, учитывай что голое приложение с микро-фреймворком может 20+ мс занимать. В целом в мире ответ API в 100-150мс считается ризонно быстрым
— Можно полностью положится на облачных провайдеров, очереди и сообщения обычно стоят очень недорого.
aso
28.12.2016 13:18+1Потенциальных сбоев в микросервисах куда больше становится. Если взять приведённый вами монолит, то там проблема будет в каком-то модуле (мы же пишем модульно, да?).
Теоретически же, какяпонимаю.
А в реале — может быть что угодно.
Микросервисы ставят «более высокие перегородки» между модулями, кмк.
Плюс, если задача легко подвергается декомпозиции — наличие большого числа микросервисов может быть выгодно с точки зрения реконфигурирования и подгонки конфигурации под текущие потребности.
AlexanderG
29.12.2016 13:32То есть, тут ОГРОМНАЯ проблема помимо кодинга ещё и в плане администрирования всего этого.
Девопсы тоже хотят кушать :)
lair
27.12.2016 12:14+9Микросервисы, как архитектурный паттерн, позволяют снизить стоимость (издержки от) неоптимального исходного кода, ускорить разработку и повысить качество сна.
В обмен на усложнение развертывания, сложности с управлением версиями и вообще резкое уменьшение количества сна у Ops.
dusterio
27.12.2016 12:161) Развертывание один раз настраивается и редко меняется, обычно. Зато развертывание изолированное — по частям, что безопаснее (вся система не рухнет из-за одного элемента)
2) Время ops зачастую дешевле времени разработчиков стоит, да и ops обычно меньше в штате :)
3) А какие сложности с управлением версиями?lair
27.12.2016 12:21+7Развертывание один раз настраивается и редко меняется, обычно.
"Обычно". То-то я регулярно что-нибудь перенастраиваю и переразворачиваю.
Зато развертывание изолированное — по частям, что безопаснее (вся система не рухнет из-за одного элемента)
… это если у вас так сделано, что микросервисы переживают отсутствие зависимостей. Что, в общем случае, далеко не всегда правда. А еще можно положить среду взаимодействия — и упадет все.
Время ops зачастую дешевле времени разработчиков стоит, да и ops обычно меньше в штате
Это пока ops не начинают будить разработчиков вопросами "тут твоя [...] упала, что делать-то?"
А какие сложности с управлением версиями?
Ну как же, есть у вас микросервис D, от которого зависят микросервисы A и B. Вы разрабатываете микросервис A, и вас не устраивает функциональность D. Вы ее дописываете. Разворачиваете D — и внезапно, B падает, потому что вы чего-то не учли.
(вот вам и "безопасное изолированное развертывание": развернули один компонент — уронили все его зависимости)
Понятно, что в этом месте люди подпрыгивают и кричат "тесты! тесты!". Но, положа руку на сердце, если люди не могут написать тесты на монолитное приложение, то почему мы думаем, что они будут их писать на микросервисы?
tangro
28.12.2016 11:42+1То-то я регулярно что-нибудь перенастраиваю и переразворачиваю.
Это называется «работа». За неё опсам платят деньги.
Это пока ops не начинают будить разработчиков вопросами «тут твоя [...] упала, что делать-то?»
О, видите, даже в этой истории у разработчика всё-таки было время поспать. Что делать? Глупый вопрос — читать логи, конечно.
Вся суть микросервисов в том, что часть работы можно эффективно переложить с плеч разработчиков на на девопсов, ещё часть — эффективно разделить между разработчиками (каждому по микросервису в зубы). В случае монолита — этот монолит лежит камнем на плечах (в лучшем случае у каждого, в худшем — у сильнейшего разработчика).dusterio
28.12.2016 12:07Ура, первый сторонник микросервисов в комментариях :))
unet900
28.12.2016 13:07Часто наблюдаю тенденцию, что когда появляется новая технология, то её начинают пропагандировать чуть ли не как панацею от всего, слабым сторонам и подводным камнях уделяют мало внимания. А в целом просто появляется еще 1 инструмент, который нужно правильно использовать и у него есть свои ниши где он эффективен.
Я с большим интересом слежу за темой микросервисов, но проанализировав проекты которые веду пришел к выводу, что издержки будут выше чем потенциальный профит.
Вот если нужно что-то сбоку к большому приложению прикрутить ИМХО микросервисы тут отличный выход.
В целом хотелось бы узнать об узких местах микросервисного подхода и его подводных камнях. Я как миним вижу потенциальные трудности в согласование команд работащиюми над разными микросервисами и возможные проблемы на каналах связи. Хочется взвешенного подхода, который уделяет слабым сторонам не меньше внимания чем сильным.dusterio
29.12.2016 03:42Думаю, что основной минус (подводный камень) — это усложнение архитектуры. Архитектор приложения должен понимать основы devops & работы сетей, облачные технологии.
Самим командам работать станет проще — с их точки зрения достаточно написать небольшое приложение, удовлетворяющее спецификациям API.
В принципе, все паттерны разработки, все «крутые» фишки ООП или функционального программирования — это всегда улучшение эффективности разработки ценой усложнения системы.
Можно написать фронт-енд на jQuery в 1000 строк, можно на Angular в 50. Второе эффективнее и проще обновляется в будущем, но требует изучения фреймворка, понимания хороших практик программирования.VolCh
29.12.2016 10:43Не столько даже самой архитектуры, сколько усложнение разворачивания и деплоя и, зачастую, обработки ошибок — ошибка удаленного вызова более вероятна чем локального. На глобальном уровне архитектура может остаться без изменений, те же данные и их структуры, те же модули, те же потоки данных между ними, просто потоки уже не локальные.
dusterio
29.12.2016 12:42Не соглашусь с тем, что ошибка удаленного вызова более вероятна. Если разработчики те же — вероятность на меняется.
VolCh
29.12.2016 13:11Имеется в виду ошибка времени исполнения вызова. Грубо, ошибка на уровне вызова через сетевой стэк более вероятна, чем ошибка на уровне вызова локальной функции.
lair
05.01.2017 00:36Самим командам работать станет проще — с их точки зрения достаточно написать небольшое приложение, удовлетворяющее спецификациям API.
Ага, это если спецификации есть и не меняются. А они всегда меняются, и дальше начинается цирк с версионированием.
lair
05.01.2017 00:35Это называется «работа». За неё опсам платят деньги.
Разработчикам тоже за работу платят деньги. Внезапно.
Вся суть микросервисов в том, что часть работы можно эффективно переложить с плеч разработчиков на на девопсов, ещё часть — эффективно разделить между разработчиками (каждому по микросервису в зубы).
В случае монолита это тоже возможно (я прямо сейчас на это смотрю). А самое главное, микросервисы не дадут эти преимущества сами по себе, для этого еще нужны девопсы (которых надо взять), эффективное разделение и все остальное.
Дело же не в том, что микросервисы плохи, дело в том, что они не бесплатны.
maxru
27.12.2016 13:37Как обычно, просто оставлю это здесь. Читайте оригинал.
http://microservices.io/patterns/index.htmldusterio
27.12.2016 14:17Лучше тогда давать ссылку на бесплатный ebook от nginx :) Там намного больше информации
martin_wanderer
28.12.2016 12:13Ну так и дали бы. А то получается как в том анекдоте про… :)
dusterio
28.12.2016 12:23Книга на английском. Судя по количеству переводов на Хабре в последнее время — далеко не все могут читать на английском
martin_wanderer
28.12.2016 12:36+1Эта книга? А насчет переводов: умение читать на языке и умение переводить — две большие разницы. Чтобы текст понять, его не нужно и даже вредно переводить. А для перевода требуется еще и родным языком владеть на уровне несколько выше среднего.
grossws
28.12.2016 13:33+2А для перевода требуется еще и родным языком владеть на уровне несколько выше среднего.
Судя по половине переводов последнего времени на хабре, нужно владеть ссылкой на translate.google.com или копией Промпта. И старательно избегать спеллчекера.
Demtriy
27.12.2016 14:36В заголовке статьи и в тегах ни слова не говорится о том, что речь идет о сервер-сайд разработке. Как применить этот подход в рамках клиентских приложний, положим, для .Net Core (через pipe-WCF) я представляю. Но вот как делать это для мобильных приложений на Obj-C / Swift — ума не приложу. Посыл — делить все на Фреймворки и библиотеки — вряд ли можно назвать сколько бы то ни было ООП подходом. Да, можно использовать глобальные оповещения, но это треш еще больший чем «монолитная» но сбалансированная архитектура.
dusterio
27.12.2016 16:05Вы правы — я об этом даже не подумал. Среди тегов не было ничего более подходящего
Конечно, это относится как правило в коллекции back-end сервисов
http3
27.12.2016 16:05Когда вы пишете программу из 100 строк, то, в принципе, не имеет значения насколько крутой вы выбрали дизайн или насколько четко вы будете следуете модным идеям.
1. Следование модным идеям — разве залог успеха? :)
От этих модных идей программа распухнет со 100 строк до 200. :)
2. Я хз, но читать и разбираться в 100 строках говнокода — это разбираться в говнокоде.
Вероятно, вы начинаете делить исходные файлы на модели, контроллеры и так далее.
и ранее
А когда файлов стало много, вы начинаете организовывать их в папки.
То есть сначала файлы группировали от балды по папкам? :)
Фреймворки зачастую насаждают свои конвенции (правила), систематические разбиение приложения на файлы и каталоги.
Так мы уже ж разбили сами все, как следует:
Вероятно, вы начинаете делить исходные файлы на модели, контроллеры и так далее.
Это дополнительная работа, барьер, который разработчики не будут преодолевать без абсолютной необходимости.
А перед этим в монолите они без надобности вызывали что-то? :)
Так как отдельно взятый микросервис – это не более, чем класс или два, вырезанных из вашего монолитного приложения, то теперь у вас меньше, значительно меньше файлов в рамках одного открытого приложения в IDE.
Шта?
У меня 200 классов.
199 классов и 1 микросервис вряд ли чем-то помогут.
Ну и 200 микросервисов — вряд ли что-то хорошее. :)dusterio
27.12.2016 16:08У Netflix 600+ микросервисов — это ок.
Если у вас миллион пользователей, то перевести 200 классов в 200 сервисов — может быть вполне разумной идеей.
До перехода, в независимости от нагрузки, каждому классу «нужны» будут остальные 199 — остальные 199 деплоятся вместе и находятся в ОЗУ вместе. После перехода на микросервисы, у 2 классов может быть по 100 «инстансов», а у остальных 198 всего по одному (потому что они проще).
Suvitruf
27.12.2016 17:16+1Вообще забавно, да. Я про «Так как отдельно взятый микросервис – это не более, чем класс или два». Это если не считать всех зависимостей. В итоге получается 1 свой класс и сотни, если не тысячи классов из зависимостей.
VolCh
27.12.2016 20:55Так как отдельно взятый микросервис – это не более, чем класс или два, вырезанных из вашего монолитного приложения
Класс или два обычно нет смысла выделять в микросервис, если этого не требуют админы/пользователи. «Микро» в «микросервис» значит, имхо, не мало кода в сервисе, а мало ответственностей перед клиентом у сервиса, тот же SRP из SOLID, вынесенный на уровень приложения (как относительно независимой программы). А проще говоря unix-way.
то теперь у вас меньше, значительно меньше файлов в рамках одного открытого приложения в IDE.
А зато открыто несколько проектов без навигации, автодополнения и, главное, синхронизации между ними :) Чтобы развернуть дев-среду надо поднимать локально не одно приложение (и среды для него), а несколько.
В общем, использовать микросервисы ради моды и(или) ради уменьшения возможности напартачить разработчикам — стрелять себе в ноги. Звоночки о необходимости что-то выделять в микросервис должны идти прежде всего от эксплуатации/пользователей, а не от разработчиков. И то, зачастую проблемы эксплуатации решаются распределенными монолитами — ставим новые инстансы монолитов (у вас же монолит масштабируется горизонтально? он же стейтлесс?) пока эксплуатация или руководство не взвоёт, что ресурсы расходуются не оптимально, что ради одного модуля, который нужно масштабировать горизонтально, нужно разворачивать приложение полностью.
Вообще, если хочется попробовать микросервисы с прицелом на горизонтальное масштабирование, сначала начните с горизонтального масштабирования стандартной инфраструктуры (она, обычно, является готовым микросервисом, с одной-двумя ответственностями), потом самого монолита, почувствуйте прелесть распределенных систем, если максимальный опыт в них у вас заключается в вынесении СУБД на другой хост, а потом уже думайте надо ли оно вам.
Deosis
28.12.2016 11:33а разработчики там вредные и они не хотят добавлять ненужный (с их точки зрения) маршрут лишь ради того, чтобы сделать вашу жизнь проще.
Так и скажу начальнику на вопрос: «Когда будет готова новая фича?»
romanitalian_net
28.12.2016 12:04Наверняка я плохо знаком с Микросервисной архитектурой.
Но через что будет проходить связь микросервисов — по tcp/ip стеку (http запросы)?
И как при этом организовывать протоколы передачи данных между мСервисами?
Как между ними согласовывать работу (какой-то диспетчер)?
Какие параметры передавать и по какому адресу?dusterio
28.12.2016 12:061) Самые распространенные способы — это http (REST) или очереди/сообщения (tcp/udp/web)
2) Как правило создается некий реестр сервисов по типу, из которого можно всегда адрес живого инстанса (экземпляра) сервиса взять. Популярное бесплатное ПО — consul.
В Docker Cloud создается внутренняя сеть автоматически между всеми сервисами, достаточно друг друга по хостнейму стучать.
В AWS можно на мониторинг и Route53 достаточно просто привязать
VolCh
28.12.2016 14:16В общем случае ip/tcp/udp, часто http поверх ip/tcp.
Диспетчеризация сообщений и ответов на них — архитектурное решение конкретного проекта. Могут быть исключительно прямые вызовы, может быть, навскидку, какая-то шина, очередь сообщений, расшаренное хранилище типа СУБД.
Адреса определяются какими-то service discovery или просто хардкодятся в конфигах клиентов DNS-имена, а на DNS прописываются серверы.
heleo
28.12.2016 12:52А на сколько это правильно противопоставлять микросервисы монолитным приложениям? Может я конечно ошибаюсь, но как по мне понятие микросервисов в первую очередь влияет на то, как осуществляется связь между компонентами (модулями) и выносят её на более высокий сетевой-протокольный уровень.
dusterio
28.12.2016 13:09В обществе разработчиков именно так традиционно противопоставляется. Вы правы насчет сетевого уровня, правда обычно его более низким, а не высоким считают (взять ту же модель OSI). Какую выгоду это дает — будет в одной из следующих статей, это очень, очень важное преимущество для high availability или high load систем
heleo
29.12.2016 00:29Я скорее со стороны уровней абстракции от живого кода смотрю) Да, по OSI оно низковато, но если сравнивать с модулями на подобие COM, вполне себе высоко.
VolCh
28.12.2016 14:18Как по мне, главное в том, что монолит в общем случае — одна программа, модули которых общаются с собой в рамках одного процесса,, а микросервис — множество изолированных, общающихся между собой средствами IPC, включая RPC.
heleo
29.12.2016 00:42Если говорить про монолит, то лучше в данном контексте модули не употреблять, лучше компоненты. Может ввести в заблуждение) А так я с вами согласен. Основа в микросервисах это полностью изолированные отдельные процессы взаимодействующие между собой на более высоких уровнях.
donRumatta
28.12.2016 19:29Вопрос по микросервисам: как шарится общий код, например, всякие утилиты, хелперы, экстешны, которые требуются везде и всюду? Дублируется во всех подсистемах?
А контракты, через которые происходит общение между сервисами, например, те же классы-сообщения, ходящие в RabbitMQ?vearutop
29.12.2016 00:03По опыту живого проекта, общий код выделяется в библиотеки которые потом доставляются менеджером зависимостей (например composer для php, или glide для golang). Зависимости актуализируются согласно семантическому версированию.
Что касается контрактов, для REST есть спецификация описания интерфейсов swagger, из неё можно сгенерировать клиентскую библиотеку (методы и структуры) и внедрить её как зависимость.
В общем случае (rabbitmq, protobuf) протокол взаимодействия (методы и структуры) должен быть описан в клиентской библиотеке.
dusterio
29.12.2016 03:28+1Да, дублируются. 10-15 лет назад это бы считалось минусом, а сегодня с текущими ценами на пространство и хостинг — всем уже без разницы. Поднимите пустой проект на React — у вас зависимостей на 500-700 мегабайт скачается (это не комплимент обществу Node :). Один дополнительный хелпер или утилита не сделают погоды вообще :)
У меня каждый микросервис экспортирует документацию в формате Swagger (как и у vearutop), а данные которые полезны более, чем одному сервису транслируются в топиках SNS в формате JSON. Нет никакой сериализации объектов, так как используются разные языки и фреймворки.
Как сделать контракт на эти JSON сообщения — наверное, можно документировать формат + покрыть тестами в сервисах, которые их публикуют. Можно, наверное, и обертку для каждого языка реализовать с поддержкой версий
VolCh
29.12.2016 10:55Если микросервисы на базе одного стека технологий, то можно просто выделить в обычные библиотеки.
Если на разных, то приходится дублировать структуры/логику, но лучше выделить или перенести логику в один микросервис, дублируя только его интерфейсные структуры данных. Впрочем и если одинаковый, то вместо либы с логикой можно сделать микросервис, а в либе оставить только структуры данных типа dto.
vearutop
29.12.2016 00:23+1Городить микросервисы имеет смысл в условиях неразберихи и переизбытка разработчиков. Таким образом можно явно распределить ответственность между командами и улучшить диагностируемость системы (мониторинг компонентов, хелсчеки).
Крайне важным при этом становится соблюдение контрактов и поддержка обратной совместимости (с версированием интерфейсов).
Процесс миграции и разделения монолита может занять годы.
Что касается масштабирования, хороший монолит не слишком уступает тут сервис-ориентированной архитектуре. Как правило все упирается в централизованные хранилища и диспетчеры.
Действительно полезное свойство — параллельные композиционные релизы, когда из-за ошибки в одной задаче, не приходится перестраивать весь реализ.
Не стоит рассматривать СОА как способ оптимизации производительности, межкомпонентные задержки и блокировки скорее всего ухудшат общую скорость.vearutop
29.12.2016 00:29Отдельной головной болью может стать обеспечение транзакционности в рамках всего приложения, а также трассировка кто кого куда вызвал при дебаге.
dusterio
29.12.2016 03:34Для этого есть стандартные решения, но это, безусловно, усложнение
СОА может повысить производительность для проекта с очень несимметричной нагрузкой вроде YouTube, где сервисов по транскодингу надо в сотни раз больше, чем сервисов по, скажем, публикации комментариев.
Так как шлюз API может асинхронно выполнять запросы к разным сервисам — здесь тоже можно выиграть в производительности, в зависимости от проекта.
dusterio
29.12.2016 03:21+1«Что касается масштабирования, хороший монолит не слишком уступает тут сервис-ориентированной архитектуре. Как правило все упирается в централизованные хранилища и диспетчеры.»
Все-таки уступает по себестоимости и эффективности. Масштабируя монолит, ты масштабируешь _все_ элементы приложения, а масштабируя микросервисы — только те, которым сейчас нужно больше ресурсов. А раз мы поднимаем «машину» по-меньше — то снизятся расходы на облачный хостинг, да и будет очевиднее, что нуждается в оптимизации, а что нет.
ImLiar
29.12.2016 00:29Ой, камон.
Основная суть микросервисов — это масштабирование на бизнес-уровне.
Пусть в компании X определена общая политика — все микросервисы должны предоставлять REST API. Вот книжка с good practice, следуйте.
Теперь каждая команда обозначает API через какой-нибудь swagger/graphql/whatever и имплементирует уже как хочет. Полная независимость разработки между командами, никаких баундов. Технологии и языки вольна выбирать команда, ответственность за продукт end to end, бизнес-домен как зона ответственности команды и продукта.
Единственное правило — не ломать API. А дальше уже скейлите, заливайте огрехи баблом и т.п. Когда у вас на фронте все рендерится по несколько сотен мс, не пофиг ли, что там на бэкэнде теперь оверхед на запросы между сервисами?
Теперь повторите такой же трюк с монолитом на масштабах больше 1к разработчиков. Чтоб гибко и независимо. Вот почему это работает для таких гигантов как Netflix или Spotify
Для команд по 5-6 человек на всю компанию вы на нормальный девопс убьете не меньше ресурсов, чем на, собственно, разработку микрух. Это из собственного опыта.
Хотя сейчас kubernetes + AWS сервисы и облегают задачу с service discovery, failover и т.п., но мороки все равно хватает.dusterio
29.12.2016 03:18Вы привели пример еще одного объективного преимущества :)
Я пользуюсь Docker Cloud, он очень много сам за меня делает «behind the scenes». Но мне немного повезло — я сетевой инженер в прошлом, поэтому нам не пришлось нанимать devops вообще.ImLiar
29.12.2016 11:46Преимущество только для кейса «много разработчиков @ много команд». На малых масштабах летит так себе
VolCh
29.12.2016 12:16Есть и преимущества в случае даже «один разработчик». Как минимум, деплой новой фичи/багфикса в рамках одного компонента в случае SOA/микросервисов часто приводит к даунтайму только функциональности, связанной с этим компонентом, а монолита — всего, причём значительно большей. Есть, конечно, практики нулевого даунтайма и для монолитов, но, как правило, они требуют ещё больших ухищрений и от девопсов, и от разработчиков.
ImLiar
29.12.2016 13:27Какие ухищрения? Round-robin на фронте?
На _нормальный_ девопс вороха микрух времени уйдет не меньше, чем на разработку. Для небольших команд это может быть критично, т.к. вместо имплементации фич они будут дрочиться, почему же у них на EC2 инстанс не качается докер имадж из приватного ECR. А выделять на это приходится человека, который код пишет.
У меня есть опыт работы и в небольшой команде, и в крупной компании. Есть возможность сравнить плюс и минусы подходов на практике, без теоретического «ну оно там чото скейлится, чтоб обрабатывать мои 100 запросов в час».VolCh
29.12.2016 13:48Как поможет Round-robin на фронте, когда идёт часовая миграция схемы БД?
ImLiar
29.12.2016 18:39Так а как тут поможет монолит или микруха, если БД под завязку?
Стейтлесс приложения обновляются один за другим без особых проблем. С тяжелыми базами будут проблемы вне зависимости от архитектуры системы.VolCh
29.12.2016 18:50Микросервисная архитектура поощряет, чтобы у каждого микросервиса своё хранилище было.
dusterio
29.12.2016 12:41Крупный проект == много разработчиков. Как сказано в статье, в маленьких проектах вообще не сильно важны технические исщихрения :)
VolCh
29.12.2016 13:13Бывают крупные проекты с малым числом разработчиков. Просто в проекте годами наращивается функциональность, а не меняется существующая.
AndreyRubankov
03.01.2017 13:02Микросервисы — они не про чистоту кода и простоту поддержки, они про автоматизированное/автоматическое горизонтальное масштабирование и повышение доступности (availability) системы.
Микросервисы подымают ошибки на новый уровень — на интеграционный. То, что раньше было проблемой в «монолите» на стыке абстракций, теперь находится на стыке абстракций в двух или более микросервисах, которые могут поддерживаться разными командами (код везде идеальный, а интеграция будет содержать не тривиальную ошибку).
Перед выбором микросервисов стоит четко понимать цели, сложности и возможные последствия этого выбора.
Если нужно сделать что-то хорошее и производительное — это «монолит».
Если нужно сделать что-то сложное, но масштабируемое — это микросервисы.
ps: Судя по прикрепленным изображениям Вы заменили спагетти-код на спагетти-микросервисы.dusterio
03.01.2017 14:32Я нигде не писал, что микросервисы – это про чистоту кода ;-) Мой тезис, что цена плохого кода в микросервисах ниже. Причины Вы сами частично назвали только что.
Про спагетти, вероятно, неудачные картинки. В Интернете ходит популярная картинка, где микросервисы с равиоли сравнивают (спагетти — плохой код 90-ых, лазанья — слои в MVC/монолитах, равиоли — микросервисы)AndreyRubankov
03.01.2017 15:05Я нигде не писал, что микросервисы – это про чистоту кода ;-)
Значит я не правильно трактовал сравнение со спагетти кодом. В моем понимании, запутанный код ? чистый код.
lubezniy
Интересно, как работают микросервисные проекты в условиях high load? Сколько дополнительных вычислительных ресурсов уходит на связи между ними?
dusterio
Микросервисы особенно эффективны в условиях high load — поэтому на них и перешли сайты с высокой нагрузкой вроде Netflix и Amazon. Расход на RESTful или очереди ничтожен, зато масштабировать конкретный сервис (который создает нагрузку) намного эффективнее, чем масштабировать весь монолит (включая ненужные части)
Представьте, что делаете сайт вроде YouTube. Естественно предположить, что на транскодинг файлов будет больше всего ресурсов уходить — логично иметь намного больше инстансов (копий) микросервиса по транскодингу, чем скажем API комментариев. Микросервисы тут идеально вписываются