Хабрахабр

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

Привет, Хабр!

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть