Считается, что запуск микросервисов изначально затратнее по времени, чем монолит, и наш опыт это подтверждает. Однако, если следовать проверенным процессам, эти затраты можно минимизировать. Делюсь лучшими практиками и составляю чек-лист запуска.

В red_mad_robot мы делаем полный цикл разработки ПО, в том числе полноценные бэкенды. Оставим за скобками холиварную тему «Монолит vs Микросервисы», и представим, что выбрана микросервисная архитектура. Отмечу, что многие пункты не содержат прямого совета делать только так и никак иначе, но призывают задуматься над выбранным решением. Также важно иметь в виду, что они могут быть очевидны или нерелевантны уже запущенным системам с налаженными процессами. Здесь речь пойдет именно про запуск новой системы, и советы как быстрее принять решение и ничего не забыть.

1. Монорепозиторий vs мультирепозиторий 

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

В случае монорепозитория у нас появляется возможность унифицировать стадию сборки проекта (например, для Docker) и вести общий файл зависимостей для всех частей системы. Это ускоряет добавление новых микросервисов. По умолчанию мы выбираем монорепозиторий, если изначально не видим выгоды от мультирепозитория. С ростом количества микросервисов вопрос деления становится все более актуальным, но при общем их числе в 20–30 монорепозиторий весьма удобен. 

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

Микросервисная архитектура в самом простом виде предполагает набор бизнесовых сервисов (например, «Каталог книг» и «Продажи»), а также сервис Gateway, который предоставляет API клиенту и знает, как получить нужные ему данные. Конечно, Gateway лишь распространенный паттерн, но можно обходиться и без него; речь пойдет про взаимоотношения микросервисов между собой в целом. Представим, что клиенту необходим список книг, с указанием количества продаж каждой книги за месяц. Здесь можно использовать два варианта работы — оркестрацию и хореографию. 

Оркестрация похожа на обход графа «в ширину»: сначала мы в сервисе Gateway получаем список книг из «Каталога», а затем по ID книг извлекаем нужные данные из «Продаж», объединяем их и отдаем клиенту. Если нам потребуются еще данные по пользователям, оформившим покупку, мы сделаем дополнительный вызов из Gateway. 

Хореография похожа на обход графа «в глубину». В таком случае вся информация для Gateway целиком подготавливается в «Каталоге», где она обогащается нужными данными из «Продаж». В свою очередь «Продажи» предварительно также могут запросить что-то из смежного микросервиса для подготовки ответа, и так далее по цепочке.

Подход оркестрации уменьшает связность микросервисов, но быстро приводит к разрастанию Gateway, необходимости добавлять в него бизнес-логику и соблазнам. Самый простой пример — появление условий для сбора данных, когда в зависимости от параметров или состояния объекта мы выбираем, откуда и какие данные хотим получить. В итоге рискуем получить God-object и точку отказа всего приложения. Хореография позволяет держать Gateway максимально простым, но повышает связность внутренних сервисов. В своей работе мы балансируем между этими двумя подходами, но точка равновесия смещена вправо к хореографии.

Удобство API важнее сложности внутренней реализации. Ещё одно важное правило: API микросервиса нужно проектировать  на основании потребностей в данных других микросервисов, которые его вызывают. Так мы везде сохраняем принцип «обхода в глубину», снижаем риск циклов или повторного запроса одних и тех же данных. 

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

3. Унифицируйте подходы

Стоит сделать шаблонный микросервис и заложить в нем:

  • договоренности по именованию в коде;

  • правила логирования;

  • правила построения API;

  • архитектуру и слои: модели, схемы, сервис, транспорт;

  • формирование конфигурации;

  • правила кодогенерации, если они предполагаются;

  • артефакты развертывания, например, манифесты k8s, или пример контейнера в docker-compose;

  • тесты, в идеале отдельно для бизнес-логики и отдельно для сообщений на уровне транспорта;

  • некоторые базовые операции-утилиты, которые будут использоваться несколькими микросервисами, например, работа с пользователем и любимые таймзоны.

Всё вышеперечисленное полезно описать в документе вроде README, это всегда помогает при вгрузе новых людей в проект. На первом этапе важно выделять достаточное время на код-ревью и следить за соблюдением всех этих подходов. С развитием проекта они растиражируются, договоренности закрепятся и вы перестанете про них думать.

4. Подумайте над межсервисной авторизацией

Самое простое — сделать Gateway единой точкой входа, и делегировать ему все вопросы аутентификации и авторизации: доверять запросам от Gateway и сразу оттуда считывать информацию о юзере, по крайней мере его id. 

Этого будет достаточно? По нашему опыту, лучше сразу делать проверку данных через JWT. Это вопрос больше не про безопасность, а про защиту от случайных ошибок в коде, когда один микросервис случайно заменяет user_id инициатора. Такое, к примеру, встречается в запросах для админок, когда надо выполнить операцию для какого-то юзера от имени другого юзера.

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

5. Настройте процессы CI/CD

Сборка приложения не должна требовать от разработчика каких-либо усилий. Но когда микросервисов много, встаёт вопрос ускорения всех стадий. Мы используем подход liquid software, когда код выкатывается мелкими проверенными порциями сразу в master, а оттуда автоматически на dev контур.

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

6. Подумайте над переменными окружения

Существуют специально предназначенные инструменты для работы с энвами, но далеко не всегда бывает время развернуть их на старте. Переменные окружения в файлах доступны с самого начала :). Подход  «один файл на одну среду — dev, stage, prod» быстро приводит к огромному нечитабельному списку. Более удобное решение — один файл на один микросервис. Но оно приводит к дублированию переменных; логично вынести общие в еще один файл. В том числе в него же выносим внутренние адреса для общения между собой. 

Поскольку обычно помимо конфигурации в окружение попадают и различные секреты, эти файлы не сохраняют в репозиторий, а помещают, например, в Gitlab CI Variables или Github Environment Secrets. Позаботьтесь о разработчиках, которые будут подключаться в будущем — сделайте шаблонный .env с необходимыми для запуска тестов переменными, и опишите в README весь процесс добавления новых энвов. Так вы избежите банальной проблемы — загрузили локально последние обновления и тесты перестали проходить, хотя у других все в порядке. Этот же .env можно использовать и в процессе CI.

7. Обратите внимание на методы API, возвращающие вложенные данные

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

Рассмотрим пример из пункта 2 с «Каталогом» и «Продажами». 

Допустим, у нас GraphQL. Мы создаем type Book { title: String! }, у которого объявляем поле sold_count — количество проданных экземпляров. Фронтенд сам решает, какие поля ему нужны — он может запросить sold_count, а может и не запросить. Скорее всего, вы прокидываете выбранные поля как Only в sql-запрос в базу. Нужно отделить атрибут sold_count и явно обработать ситуацию его наличия или отсутствия, чтобы не ходить лишний раз в микросервис «Продажи» при вызове Book. А если помимо sold_count у нас появляется еще одно поле из «Продаж», скажем — выручка, то непонятно, обрабатывать поле также отдельно или писать сложную логику, чтобы делать первый эффективный запрос.

В случае с REST проблема аналогичная, только более явная. Мы понимаем маршрут сбора данных с микросевриса для каждого запроса. Но фронтендеру могут понадобиться Books в каких-то сценариях вместе с sold_count, а где-то без этого поля. Для оптимального выполнения нам нужно либо заводить схемы наподобие BookShort и BookFull, либо делать несколько похожих методов — здесь снова нет однозначного рецепта. Но точно стоит уделить внимание информированию фронтенда — в каком случае что использовать.

Теперь поговорим про многовложенность. Допустим, у продаж есть связь «покупатель», которая ведет в третий микросервис — «Пользователи». У книги есть список комментариев, каждый из которых, в свою очередь, также ведет к «Пользователям». Появилось желание в одном запросе получать список книг и вывести список комментариев к книге вместе с информацией о каждом покупателе. В случае использования REST здесь явно прослеживается пересечение двух вызовов к «Пользователям». Разумно их объединить в один, а затем смапить данные в нужных местах.

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

8. Создайте команду

Исторически микросервисы возникали как результат операции «распилить монолит», а типичным признаком её приближения служила нелинейно возрастающая сложность разработки. Тем не менее, микросервисы — удел не только крупных компаний с большими командами. Этот подход успешно применим и на старте проектов.

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

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

Комментарии (24)


  1. AlexSpaizNet
    11.11.2021 12:15
    +12

    Из личного опыта - если это стартап, не делайте свою жизнь и жизнь того кому придется поддерживать это микро сервисное чудо через 3-5 лет когда бизнес модель измениться 2-3 раза, когда стартап только начинает понимать что у него core, какая у него нагрузка, планы на будущее в плане масштабируемости (масштабируемости бизнеса) и где ему реально нужны плюшки от микросервисов.

    Это не значит что нужно писать монолит-какашку. Начните с монолита, но с нормальной архитектурой. Не нужно писать спагетти. Разделите на модули, сервисы, отделите логику от отображения и стораджа (3tier, Onion, whatever), и в будут сможете малой кровью вынести core в микро сервисы.

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

    Что бы вам не говорили, микросервисы это сложно.


    1. as_serg Автор
      12.11.2021 11:09

      Действительно, микросервисы это сложно. Действительно, стартовать проще монолит. Я не призываю никого начинать новые проекты на микросервисах в этой статье.

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


  1. korsetlr473
    11.11.2021 12:17

    Как и где вы джойните данные для сайта в такой архитектуре? например для отображении публикации

    нужно взять статью из сервиса "статьи"

    и внизу отобразить "профиль автора" который хранится в сервисе "юзеры"


    1. vsespb
      11.11.2021 12:31

      Не очень понятно в чём сложность и каверзность вашего вопроса. На одной странице много статей с профилями?


      1. korsetlr473
        11.11.2021 12:40
        +1

        пока ничем сейчас я прощупаю какие методы они применяют , а потом уже сложные вопросы пойдут.

        методы такие как :

        join на клиента

        join на gateway

        join на основе cache при добавлении записей

        стриминговый join пререндеринг такие как join kafka stream


        1. as_serg Автор
          11.11.2021 14:56

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


          1. sswwssww
            11.11.2021 19:29
            +1

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


            1. as_serg Автор
              12.11.2021 11:14

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

              В случае монолита и явном вызове функции приведенный вопрос равносилен тому, что вы меняете сигнатуру - и не озадачиваетесь проверить, а в каких местах она используется. Проще просто не менять сигнатуру. Независимость достигается именно разграничением границ и сохранением их неизменности


              1. sswwssww
                12.11.2021 16:33

                Переформулирую вопрос. Что случится с сервисом "статьи" если сервис "юзеры" упадет? А если взять из вашей схемы сервис "пользователи" и уронить его - перестанут корректно работать сервис "продажи" и в следствии сервис "каталог". Получается у ваших микросервисов структуры данных жестко связаны друг с другом. Приходится так же думать об их согласованности(кстати, как у вас с этим дела?). В таком случае не лучше и легче ли объединить все в один микросервис, но раскидать по разным компонентам с межпроцессорными взаимодействиями?
                Я не в коем случае не указываю как делать, я скорее ученик в этом плане, хочу узнать ваше мнение/опыт :)


                1. as_serg Автор
                  12.11.2021 17:17

                  Что случится с сервисом "статьи" если сервис "юзеры" упадет?

                  В самом простом случае, если не озадачиться, то упадет. Но никто не мешает программно руками обрабатывать внутреннюю ошибку и 1) заполнять поля дефолтными/пустыми значениями 2) придумать механизм трансляции ошибки в ответе рядом с основной структурой.

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

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

                  Также в соседней ветке с AlexSpaizNet я отметил, что я не против шины, это понятный резонный механизм, но стоит ли его везде тащить на старте? Мне это видится закономерным этапом развития системы.


          1. AlexSpaizNet
            11.11.2021 23:19

            Отдавать предпочтение нужно асинхронной коммуникации, CQRS, а не синхронным вызовам других сервисов.

            Плакать хочется... Где перформанс? Эвэилабилити? Независимая разработка? Скейлинг? Или это проекты на 2 посетителя в день? Ну не нужны вам тогда микросервисы вообще...


            1. as_serg Автор
              12.11.2021 11:21

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

              Обрисуйте задачу. Я отвечал на вопрос - как отобразить данные для простого запроса клиента, которому нужно собрать их из двух разных микросервисов. Вы ответили на вопрос, который лишь у вас в голове


              1. AlexSpaizNet
                12.11.2021 16:01

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

                Почитайте про DDD, CQRS.

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

                Например, вам нужно вернуть в апишке статью и данные автора. Сервис статей и сервис авторов находятся в разных доменах.

                Вместо того чтобы в реалтайме ходить на 2 сервиса, вы заранее подготавливаете вьюшку где храните данные статьи и данные автора (только те данные что реально нужны, не весь объект автора который может содержать сотню полей в собственном домене).

                Подписываетесь на изменения автора. Если автор изменил фамилию - ваши сабскрайберы обновляют вьюшку.

                В итоге, если сервис авторов крашится, или еще чего - на уже существующий функционал это не влияет.

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

                Ну это в кратце...

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

                Если кто-то из другой команды ломает свой сервис, то у меня сломается все, потому что мой апи напрямую зависит от чужого апи.

                А что если у меня резкий скачок нагрузки, и мой сервис отлично авто-скейлится, но тот другой сервис не умеет или умеет но плохо? Время ответа моего апи будет зависеть от времени ответа чужого апи.

                А что если мои запросы убивают тот второй сервис потому что он был не готов к таким фильтрам и сортировками? Хороше, он добавит индексы для меня, и еще +100500 клиентов, у убьет свой перформанс на write side .

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

                Тут конечно стоит упомянуть eventual consistency...


                1. as_serg Автор
                  12.11.2021 16:53

                  В итоге, если сервис авторов крашится, или еще чего - на уже существующий функционал это не влияет.

                  На функционал да, но данные будут неактуальными, это лишь скрытие проблемы. Что лучше при ошибке обновления данных - оставить то же значение или показать пустое значение / текст ошибки?

                  Если кто-то из другой команды ломает свой сервис, то у меня сломается все, потому что мой апи напрямую зависит от чужого апи

                  Никто не запрещает сделать простую обработку и возвращать частичные данные при отказе сервиса нижестоящего сервиса.

                  Время ответа моего апи будет зависеть от времени ответа чужого апи.

                  Это правда.

                  А что если мои запросы убивают тот второй сервис потому что он был не готов к таким фильтрам и сортировками?

                  Хороший признак того, что данные стоит децентролизовать и хранить у себя

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

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

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


                  1. AlexSpaizNet
                    12.11.2021 17:57

                    На функционал да, но данные будут неактуальными, это лишь скрытие проблемы. Что лучше при ошибке обновления данных - оставить то же значение или показать пустое значение / текст ошибки?

                    Это не сокрытие проблемы. Это часть анкаплинга и релаябилити. Как пример, автор изменил свое имя, и мы не проапдейтили статьи, потому сервис авторов изменил структуру событий и мы не может их обработать. Юзеру пофиг. Если имя обновится через час когда баг будет пофиксен, это равносильно тому что если бы бага не было с самого начало, а сам автор решил бы изменить свое имя на 1 час позже.

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

                    Вы когда на сайте банка заходите, и вам зашла зарплата, и вы сделали какие-то покупки, вам почти всегда показывают устаревшие данные.

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

                    Никто не запрещает сделать простую обработку и возвращать частичные данные при отказе сервиса нижестоящего сервиса.

                    Вы мыслите как программист а не как домейн эксперт. В большинстве случаем старые данные намного ценнее странички с пустыми данными.

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

                    Именно. Именно в этом случае вы реально получаете отдачу от микросервисов. И да - это сложно. Коммуникация между сервисами должна быть асинхронной.

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

                    Да, поэтому там свои решения в виде схема реджистри и коррапшен лэйер и т.д. И опять же, это сложно.

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

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

                    з.ы.

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

                    Но я прекрасно понимаю что есть трейдофы, и иногда приходится ради фронтендеров городить backend for frontend, хоть я их и не люблю всей душой.

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

                    З.Ы.

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

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


                    1. as_serg Автор
                      12.11.2021 18:30

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

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

                      Тем более обидно когда на том стороннем сервисе данные меняются очень редко, а мне эти данные нужны всегда.

                      Да, еще один хороший аргумент для децентролизации данных или заведения кеша

                      Любая проблема на этом сервисе влияет на всю систему. Это реальная жопа.

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

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


  1. minpor
    11.11.2021 14:20

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


    1. AlexSpaizNet
      11.11.2021 23:25

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

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


      1. minpor
        12.11.2021 00:24

        так и делали в былые времена


        1. LaRN
          12.11.2021 09:05

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


          1. minpor
            12.11.2021 09:07

            делю базу на мастер и слейв. Пока хватает! Монолит у меня тоже есть, но он и не дико нагружен, хватает мастера


          1. as_serg Автор
            12.11.2021 11:58

            хоститься в облаке)

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


            1. minpor
              12.11.2021 12:16

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


    1. as_serg Автор
      12.11.2021 11:46

      Индексы же имеют отношение к хранению данных, а значит речь в первую очередь подразумевается БД. Ее обычно имеет смысл держать на отдельной машине, потому что база всегда ресурсы требует, независимо от архитектуры. Разница лишь в том, что при монолите у вас скорее всего будет развеситая схема с кучей констрейнтов и внешних ключей, а в случае микросервисов - набор слабосвязанных таблиц, в которых нужные констрейнты не существуют, а реализуются программно, и достигается "согласованность в конечном счете". Да, в монолите проще написать неэффективный SQL, который будет долго исполняться. А в микросервисах вы вообще просто данные потеряете и база будет несогласована.

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