Микросервисы: что это, зачем это и когда нужно их внедрять

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

    Закон Конвея и связь между бизнесом, организацией и информационной системой


    Я в очередной раз позволю себе процитировать:
    «Любая организация, которая проектирует какую-то систему (в широком смысле) получит дизайн, чья структура копирует структуру команд в этой организации»
    — Melvyn Conway, 1967
    На мой взгляд этот закон скорее соотносится к целесообразности организации бизнеса, нежели напрямую к информационной системе. Поясню на примере. Допустим, у нас есть достаточно стабильная бизнес возможность с жизненным циклом такой длительности, чтобы имело смысл организовать предпринятие (это не опечатка, но мне очень нравится этот термин, который я утащил) Естественно, что обеспечивающая система этого бизнеса будет организационно и процессно соответствовать этому бизнесу.

    Бизнес-ориентированность информационных систем




    Поясню на примере. Допустим наличие бизнес-возможности по организации бизнеса по продаже пиццы. В V1 версии (назовем ее доинформационной) компания представляла собой пиццерию, кассу, службу доставки. Эта версия была долгоживущей в условиях низкой изменчивости окружающего мира. Затем на смену пришла версия 2 — более продвинутая и умеющая использовать информационную систему в своей основе для бизнеса с монолитной архитектурой. И тут, на мой взгляд возникает вот просто ужасная несправедливость по отношению к монолитам — якобы монолитная архитектура не соответствует доменной модели бизнеса. Да будь это так, система бы не смогла работать вообще -в противоречии с тем же законом Конвея и здравым смыслом. Нет, монолитная архитектура полностью соответствует бизнес-модели на данном этапе развития бизнеса — я конечно имею в виду этап, когда система уже создана и введена в эксплуатацию. Совершенно замечательный факт, что вне зависимости от архитектурного подхода и сервисно-ориентированная архитектура версии 3 и микросервисная архитектура версии N будет работать одинаково хорошо. В чем подвох?

    Все течет, все изменяется или микросервисы — средство борьбы со сложностью?


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

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

    Преимущество использования гетерогенного стека тоже является сомнительным. Я не буду спорить, что такое тоже возможно, но в реальности редко встречается (Забегая вперед — это должно иметь место быть — но скорее как следствие, нежели преимущество).

    Жизненный цикл продукта и жизненный цикл сервиса


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

    Перейдем на следующую ступень эволюции информационных систем — на сервис-ориентированную архитектуру SOA. Итак, в какой-то определенный момент мы выделили в своем продукте долгоживущие сервисы — долгоживующие в том смысле, что при переходе между версиями продукта есть шансы что жизненный цикл сервиса будет дольше, чем жизненный цикл очередной версии продукта. Логично было бы не изменять их вообще — нам важна именно скорость перехода к следующей версии. Но увы, мы вынуждены вносить постоянные изменения в сервисы — и здесь у нас все годится, и практики DevOps, и контейнеризация, и прочее — все что в голову придет. Но это все еще не микросервисы!

    Микросервисы как средство борьбы со сложностью… управления конфигурацией


    И вот тут мы можем наконец-то перейти к определяющей роли микросервисов — это подход, упрощающий управление конфигурацией продукта. Детальнее говоря, функция каждого микросервиса описывает именно бизнес-функцию внутри продукта согласно доменной модели — а это уже вещи, которые живут не в короткоживущей версии, а в долгоживущей бизнес-возможности. И переход к следующей версии продукта происходит буквальным образом незаметно — вы изменяете/добавляете один микросервис, а возможно и просто схему их взаимодействия и внезапно оказываетесь уже в будущем, оставляя за бортом плачущих конкурентов, продолжающих прыгать между версиями своих монолитов. Теперь представьте себе, что имеется достаточно большой объем микросервисов с заранее определенными интерфейсами и бизнес-возможностями. И вы приходите и строите структуру вашего продукта из готовых микросервисов — просто рисуя диаграмму например. Поздравляю — у вас появилась платформа — и теперь вы можете себе бизнес накликать. Мечты, мечты.

    Выводы


    • Архитектура системы должна определяться жизненным циклом входящих в нее компонентов. Если компонента живет в рамках версии продукта — нет смысла увеличивать сложность системы, применяя микросервисный подход.
    • Микросервисная архитектура должна основываться на доменной модели — по той причине что бизнес-возможность наиболее долгоживущая область
    • Практики доставки (практики DevOps) и оркестрации имеют для микросервисной архитектуры одно из важнейших значений — по той причине, что увеличение скорости изменения компонентов предъявляет повышенные требования к скорости и качеству доставки

    Похожие публикации

    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

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

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


      Не пробовали разобраться во взаимодействиях в Rails монолите с парой сотней моделей, которые находятся в одной папке «models»? Ну это так, один пример. Везде тут очень слабые аргументы.
        +4
        А разбиение на пару сотен микросервисов поможет? Я же не говорю про плохо спроектированные монолиты. Плохо написать можно все, что угодно. У меня аргументация с точки зрения продукта. Приводите свои, для этого и писал.
          +4

          Это вопрос только культуры разработчиков. В том же Rails вполне можно раскидать модели по разным неймспейсам, и если разработчики этого не сделали — сами виноваты. И микросервисы тут ни при чём.

          +2
          Серьезно, очевидное взаимодействие в рамках монолита и гомогенного кода кажется сложным?

          Очевидное — не кажется. Вот только оно часто не очевидное.


          Преимущество использования гетерогенного стека тоже является сомнительным.

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

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

              Тем не менее польза есть. Скажем, на некоторых языках реализовывать что-то вроде вебсокетов проблематично, а на Go или Node.js очень просто получается.

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

                И зачем у вас одна и та же команда пишет микросервисы на разных стеках?

                  0
                  Ткните носом, где я о таком написал.
                    0

                    А если не пишет, тогда и проблемы с переключением между стеками у вас нет.

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

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

              +2

              Для меня микросервисы — это про масштабируемость и инкапсуляцию. Но не только про "дефолтную" (горизонтальную по вычресурсам) масштабируемость и "ООП" инкапсуляцию.


              Микросервисы позволяют масштабировать разработку, позволяют большему количеству разработчиков работать над разными задачами параллельно. Позволяют инкапсулировать легаси код и уменьшают области влияния ошибок.

                –1
                Увы — так чаще всего не работает, ломается именно на взаимодействии микросервисов/команд. Описание программных интерфейсов в монолите работает абсолютно таким же образом — какая разница, в чем именно инкапсулируется функция — в классе или внешнем сервисе. Все, что Вы описали — это про любую архитектуру.
                  0

                  Чисто математически меньше вероятность мерж-конфликтов, чисто физически ошибка в одном процессе не приводит к падению всей системы

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

                      Я про вероятность мерж-конфликтов.

                        0
                        мерж-конфликт — это ерунда по сравнению с конфликтом взаимодействия. Мерж-конфликт обнаруживается раньше и резолвится быстрее.
                          0

                          мерж конфликт — это не про микросервисы, а скорее про способ организации [хранения] кода репо. И, да, можно УСПЕШНО разрабатывать микросервисы в рамках МОНОРЕПЫ (сюрприз, да?)

                            0
                            конечно можно. Что google что яндекс сидят в монорепах. И что?
                              0

                              Ничего, просто коллега выше начал рассуждать о том, что напрямую к микросервисам не имеет отношения

                              0

                              Нет, не сюрприз. Я именно так предпочитаю по умолчанию организовывать микросервисы :) Когда в каждой "папочке" в монорепе лежит отдельный сервис/приложение, то мержконфликты карйне редки по сравнению с монолитом.

                                +1

                                Но это же ничем не отличается от:
                                Когда в каждой "папочке" в монорепе лежит отдельный пакет/модуль/гем/библиотека т.д. которые являются частью монолита.

                                  0
                                  микросервисы взаимодействуют друг с другом через интерфейс API или брокер — так что очень даже отличается
                                    +1

                                    Я говорил про вероятность мерж конфликтов

                                      +1

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

                                        0

                                        Именно так. Поэтому давайте признаем, что микросервисы сами по себе очень мало влияют на вероятность мерж конфликтов.
                                        Как пример — https://habr.com/ru/post/499174/#comment_21548008
                                        Наличие общих библиотек и все такое


                                        Т.е. вроде как дихотомия между монолит vs микросервисы похожа на историю процедурное программирование vs ООП, только на другом уровне — на уровне инфраструктуры. Последние действительно позволяют строить более сложные системы. Но это как в анекдоте — у вас распределенная система? Ну, ок — вы имеете ВСЕ проблемы распределенных систем. Получите при этом какие-либо преимущества — вопрос (см. каждый случай отдельно)

                                    +2

                                    А в отдельной папочке лежит какой-нибудь shared или utils. А кто-то сигнатуры поправил в соседней папочке и т. п.

                          0

                          Разница возможно в том, что в монолите можно вызвать «приватный метод» используя хаки языка программирования и тем самым заложить мину, а в микро-сервисах только доступно только то что даёт REST API и не более. Куча сложности в монолитах возникает от таких заложенных мин.

                            0
                            SOA имеет аналогичное преимущество. Ограничивая доступную для использования функциональность, микросервисы и сервисы увеличивают сложность внесения изменений в целом в систему — ведь не на пустом месте возникает необходимость вызвать приватный метод, что-то же это дало. Опять же — вопрос культуры разработки. Если метод явно обозначен как приватный, и это еще прошло код-ревью, линтеры и т.п. и по итогу вылилось в продакшн систему — значит что-то не так с самой командой. Плохо сделать можно всегда и несмотря ним на что.
                              +1

                              Сложность внесения изменений как раз уменьшается, нет страха что-то поломать глобально, потому что связанность кода низкая. Условно вносишь изменения в каждом сервис независимо и даже если поломал обратную совместимость в АПИ рванет на интеграционных тестах, а не на боевой среде. Опять же выкатывать изменение можно не разом на всех, а на небольшую часть аудитории, т.е. постепенно менять старую версию сервиса на новую, сейчас контейнеры это все легко позволяют делать. Насчёт культуры разработки, она конечно есть и есть стандарты кодирования, но когда горит проект люди и не такое делают, я это к тому что с людьми можно договориться если очень нужно, чтобы на какие-то косяки закрыли глаза потому что сроки горят, нужно акты подписывать. А если кроме АПИ в коде ничего нет наружу и некуда костыль вставить, то тут уже как ни крути нужно сразу делать хорошо просто потому что по другому никак. Хотя времени на подумать требуется больше.

                                0
                                сложность внесения в отдельный сервис, не в систему. Выкатить новую версию сервиса постепенно возможность существовала всегда — практики эти появились еще до микросервисов. Что касается хаков — ну положим вызвать приватный метод или переопределить финализированный не во всех языках так просто. Опять же — я Вас удивлю, но в коде тоже можно применять архитектурные практики таким образом, чтобы минимизировать подобные случаи. И у микросервисов с событийной архитектурой вообще может не быть REST API — только шина. Куда тогда денется ваше преимущество?
                                  0

                                  Если верить классикам (например Крис Ричардсон), то для взаимодействия нужно использовать или REST API или брокеры сообщений, например Кафка или Рабит. А шина это как раз уже компонент SOA, а не микросервисов. Насчёт архитектурных практик, да все есть, но есть и возможность их обойти потому что монолит. Единственный способ избежать использования хаков, это не иметь возможности их использовать вообще. Мы сейчас в компании пилим монолит на сервисы, и из сложностей могу такие выделить: сложно по бизнесу понять, где границы т.е. как разделить функционал по сервисам, сложно спроектировать правильно АПИ, чтоб потом не переделывать, сложно сделать так чтобы сервисы масштабировались при росте нагрузки, много сложного DevOps. Т.е. много работы для архитекторов, которые проектируют АПИ и прорабатывают бизнес-процессы. Но когда процесс распилили на сервисы, утвердили АПИ и отдали в команды разработки — разработка идёт быстрее и требование к квалификации разработчиков ниже и время на погружение нового разработчика в проект меньше, от разработчика не требуется глубокого понимания предметной области, нужно просто хорошо писать код. Так что в общем разработка микросервисов — это совсем не просто, есть плюсы и есть минусы и их нужно уметь готовить.

                                    0
                                    что понимать под шиной — вопрос терминологии, суть именно в асинхронном обмене данными. Что касается вашего проекта — я правильно понимаю, что вы позиционируете микросервисы, как средство борьбы с хаками внутри проекта? Так я могу предложить способ проще — штраф в ползарплаты тому, кто написал хак и премия в четверть тому, кто этот хак заметил. Обращайтесь :) А если серьезно — обратите внимание, я основным преимуществом микросервисов обозначил их долговечность по сравнению с версиями бизнеса. У вас не получится бороться со сложностью системы используя микросервисы — во-первых, вы блокируетесь на работе архитекторов — а таковых в проекте заведомо меньше разработчиков — в итоге будете всегда жить в ожидании светлого будущего, изменения в архитектуру все равно надо вносить постоянно, и у вас не получится снизить требования к пониманию разработчиками домена — ведь плохо написать можно всегда. Сложность никуда не делась, она осталась — и монолит, и микросервисы реализуют одну и ту же бизнес-модель. Ценность микросервисной архитектуры — увеличение скорости перехода между версиями
                                      0
                                      вернее не домена — к квалификации разработчиков. К доменной экспертизе как раз-таки требования понижаются.
                                        0

                                        Штраф — это уголовка, вроде, по УК РФ. :)


                                        А микросервисы, как и множество других практик типа ООП, созданы не для уменьшения сложности, а для увеличения контроля над ней. Прежде всего по принципу "разделяй и властвуй". И это увеличивает общую сложность системы, но упрощает её изменения.


                                        Что до долговечности, то одна из стратегий перевода монолита на (микро)сервисы как раз обратная — самый часто меняющийся модуль вынести в (микро)сервис, чтобы релизы всего остального не зависели (или минимально зависели) от релизов этого модуля.

                                          0

                                          Да, так конечно тоже допустимо — но это не микросервис. Если что-то меняется часто — значит есть много бизнес-причин для его изменения — и соответственно этот модуль несёт в себе много бизнес-возможности. Вы просто выносите часто изменяющуюся часть монолита. Что касается остального, абсолютно согласен — об этом и написал.

                                            0

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

                                  0
                                  Ограничивая доступную для использования функциональность, микросервисы и сервисы увеличивают сложность внесения изменений в целом в систему — ведь не на пустом месте возникает необходимость вызвать приватный метод, что-то же это дало.

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

                                    0

                                    Потеря денег или задолженность перед инвесторами куда хуже, чем техдолг) Если не задумываться о том, что микросервисы живут по отдельности дольше, чем версии продукта, то это выброшенные деньги

                                      0

                                      Задача может быть как раз в том, чтобы версии микросервисов жили меньше чем версии монолита. Типа монолит мы не можем себе позволить деплоить чаще чем раз в 2 недели, а вот отдельный микросервис хоть несколько раз в день можем

                            +1
                            Спасибо за статью. На мой взгляд, основная проблема при распиле монолитов — конфликт между необходимостью постоянного внесения изменений в сами монолиты и необходимостью «зафиксировать» состояние системы, чтобы написать микросервис (переписать нужных кусок кода) и интегрировать его. Как решали эту проблему? Какова, на Ваш взгляд лучшая стратегия:
                            — пытаться сразу выпиливать большие и значимые куски монолита;
                            — «срывать яблоки, которые низко висят», т.е выносить то, что проще вынести;
                            — писать «на микросервисах» только новый функционал?
                              0
                              хороший вопрос. На мой взгляд один из выигрышных вариантов — для начала разобраться с бизнес-архитектурой приложения — чтобы понять, как будет выглядеть система по окончании миграции, затем зафиксировать внешний интерфейс и миграцию производить без влияния на интерфейс. Если вы правильно определили бизнес-функцию микросервиса — то вполне логично, что изменения вносить в дальнейшем придется только в одном месте, перенеся ее с монолита на микросервис. Ну и серебряной пули нет — лучшую стратегию придется определять самим — в буквальном смысле с калькулятором, определяя наилучшую комбинацию.
                                0

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

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

                                    Как по мне, то довольно хорошая идея на начальном этапе перехода на микросервисы, когда ещё инфраструктура, архитектура ещё не готовы. Если что-то пойдёт не так, то это "вы не смогли нормально сделать", а не "работало нормально, а вы сломали". Отлаживать архитектуру и продакшен инфраструктуру на активно используемой функциональности так себя идея с точки зрения рисков, по-моему.

                                      0
                                      Если что-то пойдёт не так, то это «вы не смогли нормально сделать»

                                      Это называется неумение работать с рисками.
                                        +1

                                        Это именно работа с рисками. Отказ новой функциональности, которой никто толком ещё пользоваться не начал — это минимум потерь при одинаковой вероятности отказа какого нибудь сервиса авторизации, без которого с системой работать просто нельзя. Тренируемся работать с микросервисами на тех частях системы, отказ которых вызовет минимум потерь, а вероятность отказа в целом одинакова.

                                  –3
                                  Во первых, в названии технологии не нужна часть «микро». Сервисы — они и в африке сервисы. Размер не является определяющим фактором. По факту имеем избитое маркетинговое название, навязанное разного рода рекламными кампаниями от продавцов всяких шин да брокеров.

                                  Во вторых, взаимодействие через HTTP, XML и тому подобное отнимает много времени. Если при выполнении одной бизнес-функции имеет место 50 подобных взаимодействий (а микросервисы всячески подталкивают в эту строну), то при среднем размере обмена в 10кб (что ещё оптимистично) имеем 500 кб трафика. По гигабитной сети (128мегабайт/с в идеале) получаем 4 мс затрат на одну функцию только по сетевому взаимодействию. 250 клиентов, постучавшихся одновременно, наглухо заткнут канал. Или кто-то нам рассказывал сказки про масштабируемость? Но это только канал. А ведь за каждым «микро»-сервисом норовят поставить БД, а это запись на диск, что даёт на каждый вызов уже по 10 мс (без учёта сетевых и процессорных накладных). Итого — 500мс на одну функцию. И да, при 250 одновременно стучащихся клиентах. Если время ожидания клиента зафиксировать на посредственных 2-х секундах, то имеем необходимость параллелить обработку на 60 серверов. И это без учёта необходимости реплицировать изменения в этой распределённой БД. Ну а чё жалеть, железо ведь дешёвое, и программисты довольны, можно сегодня на хаскеле, завтра на брейнфаке — лепота!

                                  В третьих, всегда и во всём нужно понимание, планирование и качественная реализация. Бездумно плодить миллионы микросервисов — путь не просто в бессмысленное перекидывание байтов с диска на диск, а в сторону полного бардака в разработке. Нужен архитектор, который выделяет главное, оставляя несущественное внутри монолита. Но при таком подходе нужна команда, которая качественно работает с пожеланиями такого архитектора. И вот эту внутреннюю кухню разработки тоже нужно понимать. Но кто из начальства её понимает? Поэтому архитектора либо нет, либо есть некто с завышенным ЧСВ и должностными инструкциями, обязывающими остальных делать ему ку. И к чему это приводит — ну понятно, миллион сервисов, бардак, дорого, долго, но зато модный тренд не упущен.

                                  Вот такая она, микросервисная задница.

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

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