Русский
Русский
English
Статистика
Реклама

Разложение монолита

Эта статья является конспектом книги От монолита к микросервисам. Материал статьи посвящен шаблонам разложения монолита.

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

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

Вырезать, скопировать или заново реализовать?

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

Рефакторинг монолита

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

Модульный монолит?

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

Шаблон: приложение Фикус-удавка

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

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

Реализация шаблона Фикус-удавка основана на трех шагах (рис. 1). Для начала нужно определить части существующей системы, которые хотите мигрировать. Затем нужно реализовать эту функциональность в новой микрослужбе. Когда новая реализация будет готова, нужно иметь возможность перенаправить вызовы из монолита в новую микрослужбу.

Рис.1 Общий вид шаблона Фикус-удавкаРис.1 Общий вид шаблона Фикус-удавка

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

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

Для того, чтобы шаблон работал как надо, необходимо, чтобы была возможность четко отображать входящий вызов интересующей функциональности, который хотите перенести. Например, на рис.2 в идеале хотелось бы вынести отправку Уведомлений пользователю в новую службу. Однако уведомления запускаются в результате многочисленных входящих вызовов монолита. Следовательно, мы не можем четко перенаправлять вызовы извне самой системы.

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

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

Пример: обратный прокси-селектор HTTP

На рис.3 изображен существующий монолит, который выставляет наружу HTTP-интерфейс. Цель: вставить обратный прокси-селектор HTTP между вышестоящими вызовами и нижестоящим монолитом.

Рис. 3 - Простой общий вид монолита до реализации "удавки"Рис. 3 - Простой общий вид монолита до реализации "удавки"

Шаг 1 если еще нет прокси-селектора, то необходимо его добавить. На этом первом шаге прокси-селектор просто позволит любымвызовам проходить насквозь без изменений.

Шаг 2 мигрировать функциональность. После установки нашего прокси-селектора на свое место можно начать извлечениеновой микрослужбы. Сам этот шаг можно разбить на несколько этапов. Прежде всего, привести базовуюслужбу в рабочее состояние без реализации какой-либо функциональности.Служба должна будет принимать вызовы соответствующей функциональности, но на этом этапе можно просто возвращать код ошибки 501 Not Implemented. Даже на этом шаге можно развернул службу в производственнойсреде. Это позволит освоиться с процессом развертывания в производстве ипротестировать службу прямо на месте.

Шаг 3 перенаправить вызовы. Только после завершения переноса всей функциональности, нужно переконфигурировать прокси-селектор с целью перенаправления вызовов, как мы видим на рис. 4.Если по какой-либо причине это не получается, то вы можете выставить перенаправление в прежнее состояние для большинства прокси-селекторов это оченьбыстрый и легкий процесс, дающий быстрый откат.

Рис. 4. Шаг 3: перенаправление вызова функциональности "Расчета заработной платы" в завершение миграцииРис. 4. Шаг 3: перенаправление вызова функциональности "Расчета заработной платы" в завершение миграции

Пример: перехват сообщений

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

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

Рис. 5 Монолит, принимающий вызовы через очередьРис. 5 Монолит, принимающий вызовы через очередь

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

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

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

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

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

Шаблон: Ветвление по абстракции

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

Он состоит из пяти шагов:

  1. Создать абстракцию для заменяемой функциональности.

  2. Изменить клиентов существующей функциональности так, чтобы они использовали новую абстракцию.

  3. Создать новую реализацию абстракции с переработанной функциональностью. В нашем случае это новая реализация будет вызывать новую микрослужбу.

  4. Переключиться на новую реализацию.

  5. Очистить абстракцию и удалить старую реализацию.

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

Шаг 2 использовать абстракцию. Теперь, когда наша абстракция создана, нам нужно изменить существующих клиентов функциональности "Уведомления", для того чтобы использовать этуновую точку абстракции, как мы видим на рис. 7.

Рис. 7 - Шаг 2: изменить существующих клиентов так,чтобы они использовали новую абстракциюРис. 7 - Шаг 2: изменить существующих клиентов так,чтобы они использовали новую абстракцию

Шаг 3 создать новую реализацию. Когда новая абстракция находится на своем месте, можно начать работу над новойреализацией вызова службы. Внутри монолита реализация функциональности Уведомления будет представлять собой просто клиента,который вызывает внешнюю службу, как показано на рис. 8. Подавляющая частьфункциональности будет находиться в самой службе. Ключевая вещь в этой точке состоит в том, что, хотя мы имеем две реализацииабстракции в кодовой базе одновременно, только одна реализация в настоящеевремя активна в системе. До тех пор, пока мы не будем довольны тем, что наша новая реализация вызова службы готова, она практически находится в спячке.

Рис. 8 - Шаг 3: Создать новую реализацию абстракцииРис. 8 - Шаг 3: Создать новую реализацию абстракции

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

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

Шаблон: Параллельное выполнение

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

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

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

Рис. 9 - Пример параллельного выполненияРис. 9 - Пример параллельного выполнения

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

Стоит отметить, что параллельное выполнение отличается от выпуска канареечного релиза. Канареечный релиз (canary
release) связан с направлением некоторого подмножества пользователейк новой функциональности, при этом подавляющая часть пользователей видит старую реализацию. Идея состоит в том, что если новая система имеет проблему,то только подмножество запросов подвержено влиянию этой проблемы.Еще один родственный метод называется темным запуском (dark launching). При темном запуске развертывается новая функциональность и тестируется, ноновая функциональность для пользователей невидима. Поэтому параллельное выполнение является способом реализации темного запуска, посколькуновая функциональность практически невидима для пользователей до техпор, пока вы не переключитесь на нее.Темный запуск, параллельные выполнения и выпуск канареечных релизов все эти методы можно использовать для верификации того, что новая функциональность работает правильно, и для уменьшения влияния, если окажется, чтоэто не так.

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

Шаблон: Сотрудник-декоратор

Что произойдет, если вы захотите вызвать какое-то поведение, основываясь на том,что происходит внутри монолита, но вы неспособны изменить сам монолит? Шаблон Сотрудник-декоратор (decorating collaborator) окажет здесь большую помощь.Широко известный структурный шаблон Декоратор позволяет прикреплять новую функциональность к чему-либо без того, чтобы лежащая в основании вещьчто-то об этом знала. Мы собираемся использовать декоратор, чтобысделать вид, что наш монолит делает вызовы нашей службы напрямую, даже еслимы на самом деле не изменили лежащий в основании монолит.Вместо перехвата этих вызовов до того, как они достигнут монолита, мы даем вызову выполняться как обычно. Затем, основываясь на результате этого вызова, мыобращаемся к нашим внешним микрослужбам. Давайте рассмотрим эту идею на примерефункционала программы лояльности для компании Music Corp (это выдуманная компания для иллюстрации концепций из другой книги автора Building Microservices)

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

Рис. 10 - Когда заказ успешно размещен, наш прокси-селектор обращается к службе Лояльности для начисления баллов клиентуРис. 10 - Когда заказ успешно размещен, наш прокси-селектор обращается к службе Лояльности для начисления баллов клиенту

С шаблоном Фикус-удавка прокси-селектор был довольно упрощенным. Теперьже он должен делать свои собственные вызовы новой микрослужбы и туннелировать отклики назад клиенту. Нужно приглядывайте за сложностью, которая сидитв прокси-селекторе. Чем больше кода вы начинаете здесь добавлять, тем больше онстановится микрослужбой сам по себе.Еще одна потенциальная трудность заключается в недостаточном объеме информациииз входящего или исходящего запроса. Например, если мы хотим присудить баллы, основываясь настоимости заказа, но стоимость заказа не ясна ни из запроса в Размещении заказа,ни из отклика оттуда, то нам потребуется поиск дополнительной информации, возможно, обратный вызов в монолит для извлечения нужной информации. Поскольку этот вызов генерирует дополнительную нагрузку и, возможно, вводитциклическую зависимость, было бы лучше изменить монолит для обеспечения необходимой информации, после того как размещение заказа будет завершено.

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

Шаблон: Захват изменений в данных

В рамках шаблона Захват изменений в данных (change data capture), вместо тогочтобы пытаться перехватывать вызовы в монолит, мы реагируемна изменения, вносимые в хранилище данных.

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

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

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

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

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

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

Пакетное копирование изменений. Вероятно, наиболее упрощенный подход написание программы, которая на регулярной основе сканирует затрагиваемую базу данных на предмет того, какиеданные изменились, и копирует эти данные в место назначения, например, с помощью cron. Главная проблема выяснить, какие данные фактически изменились с моментапоследнего запуска пакетного копирования. Некоторые БД позволяют просматривать метаданные таблиц, чтобы увидеть, когда части базы данных изменились, но этот подходдалеко не универсален и будет давать вам временные метки изменений только науровне таблицы, когда предпочтительней бы иметь информацию на уровне строк. Вымогли бы начать добавлять эти временные метки сами, но в результате объем работы значительно возрастет.

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

Вывод

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

Источник: habr.com
К списку статей
Опубликовано: 21.03.2021 10:06:16
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Программирование

Анализ и проектирование систем

Проектирование и рефакторинг

Микросервисы

Проектирование систем

Архитектура приложений

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru