Ваши распределенные монолиты плетут козни у вас за спиной

в 20:50, , рубрики: event sourcing, Анализ и проектирование систем, Блог компании Издательский дом «Питер», микросервисы, Программирование, Проектирование и рефакторинг, распределенные системы

Привет!

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

Ваши распределенные монолиты плетут козни у вас за спиной - 1

Время от времени я задаюсь одним и тем же вопросом.

Есть ли такая важная истина, в которой с вами соглашаются лишь немногие? — Питер Тиль

Прежде, чем сесть за этот пост, я долго примерял этот вопрос к одной теме, которая сегодня в серьезном тренде – речь о микросервисах. Думаю, теперь мне есть о чем рассказать; некоторые находки основаны на размышлениях, другие – на практическом опыте. Итак, рассказываю.
Начнем с одной важной истины, которая послужит нам в этом пути ориентиром как полярная звезда.

Большинство реализаций микросервисов – ни что иное как распределенные монолиты.

Эра монолитов

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

Ваши распределенные монолиты плетут козни у вас за спиной - 2

Типичная архитектура монолитного приложения

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

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

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

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

Эра распределенных монолитов

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

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

Ваши распределенные монолиты плетут козни у вас за спиной - 3

Обобщенный вид системной архитектуры после открепления сервисов биллинга и отчетности от основного монолитного приложения

Все идет по плану.

  • Команда продолжает дробить монолит на более мелкие системы;
  • Конвейеры непрерывной интеграции/доставки работают как часы;
  • Кластер Kubernetes здоров, инженеры работают продуктивно и всем довольны.

Жизнь прекрасна.

Но что, если я скажу, что прямо сейчас против вас плетутся гнусные заговоры?

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

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

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

Ваши распределенные монолиты плетут козни у вас за спиной - 4

Обобщенный пример аналитической ETL-системы (у нас в Unbabel мы назвали ее Automatic Translation Analytics)

В Unbabel мы воспользовались именно таким подходом, так как:

  • Он не слишком сильно влияет на производительность каждого микросервиса;
  • Он не требует серьезных инфраструктурных изменений (просто добавляем новый микросервис);
  • Мы смогли достаточно оперативно удовлетворить наши бизнес-требования.

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

1. Изменения данных

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

2. Необходимость обрабатывать множество разных схем данных

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

Корень всех зол

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

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

Ваши распределенные монолиты плетут козни у вас за спиной - 5

Именно такую мешанину данных представляли собой информационные потоки Linkedin по состоянию примерно на 2011 год — источник

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

Разбиваем распределенный монолит при помощи регистрации событий (Event Sourcing)

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

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

Возможно, у вас возникает вопрос:
“Как же микросервисы, порождающие события, помогут мне с решением проблемы распределенного монолита?”

Если у вас есть системы, порождающие события, то может быть и лог фактов, обладающий следующими свойствами:

  • Отсутствие привязки к какому-либо хранилищу данных: события обычно сериализуются с использованием двоичных форматов, таких как JSON, Avro или Protobufs;
  • Неизменяемость: как только событие порождено, изменить его невозможно;
  • Воспроизводимость: состояние системы в любой заданный момент времени может быть восстановлено; для этого достаточно «переиграть» лог событий.

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

Вот несколько причин, по которым лог событий кажется мне тем средством, которое помогает разбить Распределенный Монолит:

1. Единый источник истины

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

2. Универсальный формат данных

В предыдущем варианте системы нам приходилось иметь дело со множеством форматов данных, так как мы были непосредственно связаны с базой данных. В новой компоновке мы можем действовать гораздо более гибко.
Допустим, вам понравилась фотография из Instagram, которую опубликовал кто-то из ваших друзей. Такое действие можно описать: “Пользователю X понравился снимок P”. А вот событие, представляющее этот факт:

Ваши распределенные монолиты плетут козни у вас за спиной - 6

Событие, соответствующее подходу AVO (Actor, Verb, Object), моделирующий факт выбора пользователем понравившегося снимка.

3. Ослабление связи между производителями и потребителями

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

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

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

Автор: ph_piter

Источник

* - обязательные к заполнению поля