SkillGridИзучай computer science на практике

Одна программа или много: монолит и сервисы

Разделение системы на сервисы — это обмен, а не улучшение. Этот урок о том, что даёт каждая форма и чего она стоит.

15 мин · 48 XP

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

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

UserOne programOne databasesearch for a bookread the cataloguerowsresultsone process, one deploy, one log file
                     MONOLITH                  SERVICES
                   ----------------------    ----------------------
processes to run   one                       many
deploys            one for everything        one per service
where you debug    one stack trace           a trace across five logs
scaling            copy the whole thing      copy only the busy part
data               one database, easy joins  split, joins become calls
failure            one crash hurts all       one crash stays containedКаждый выигрыш слева превращается в затраты справа; ни один столбец не достаётся бесплатно.

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

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

Самая глубокая из этих затрат — та, которую новички совсем не ждут: сеть не бесплатна и не надёжна. Внутри одной программы вызов функции настолько дешёв и настолько надёжен, что ты перестаёшь о нём думать. Между двумя сервисами каждый вызов может занять в сто раз больше времени, может сломаться сам по себе и может тянуться так медленно, что съест поток и утащит за собой того, кто вызвал. Код, который был простым перебором пяти элементов, превращается в пять последовательных сетевых обходов. Архитектура на этом уровне — в основном изучение того, какие вызовы ты можешь позволить себе делать через границу.

Практика

  1. Оформление заказа вызывает сервис склада, чтобы забронировать товар. Что здесь может пойти не так, чего не могло бы случиться внутри одной программы?

    Подсказки
    • Вызов функции внутри одной программы либо возвращает результат, либо выбрасывает ошибку: ответить наполовину он не может.
    • Удалённый вызов идёт по сети, а у сети есть и третий исход.

    15 XP

  2. Сопоставь каждую проблему с той формой, в которой она проявляется сильнее.

    Подсказки
    • Боль при отладке зависит от числа мест, куда приходится заглядывать, а не от объёма кода.
    • Боль при масштабировании — от того, что приходится копировать и те части, которые не нагружены.

    15 XP

  3. Расставь по порядку честный путь от одной программы к нескольким сервисам.

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

    18 XP