Гай Харрисон, Эндрю Маршал, Чарльз Кастер

Архитектура распределённых

транзакционных приложений

Исходный текст, 2023 / Русский перевод, 2023


Оглавление

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

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

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

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

После прочтения этого отчёта, мы надеемся, что вы сможете хорошо разобраться в мотивах бизнеса и технологических соображениях для применения современных распределенных архитектур и будете знакомы с архитектурными паттернами и процессами разработки и эксплуатации программного обеспечения, наиболее широко применяемыми в отрасли. В частности, вы будете хорошо подготовлены к пониманию роли технологий и паттернов, таких как Docker, Kubernetes и распределённые транзакционные базы данных, в современных распределённых архитектурах.
Зачем нужны распределённые
транзакционные приложения?
Как писал Марк Андриссен более десяти лет назад, «программное обеспечение пожирает мир». В современном мире наибольший аппетит у распределённого программного обеспечения. Все чаще именно распределённое программное обеспечение обеспечивает работу приложений, управляющих нашим обществом и помогающих в нашей цифровой жизни.
События последни нескольких лет, безусловно, подчеркнули важность распределённого программного обеспечения.

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

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

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

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

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

Надёжность (reliability)
Распределённые системы являются по своей сути более надёжными, чем монолитные системы, поскольку они не имеют единой точки отказа.

Масштабируемость (scalability)
Монолитные системы испытывают трудности при росте потребности в вычислительных ресурсах. В отличие от них, хорошо спроектированные распределенные системы могут масштабироваться путём добавления новых узлов или экземпляров сервисов.

Гибкость (elasticity)
Гибкость подразумевает, что приложение также может «сворачиваться», освобождая ресурсы, когда снижается нагрузка на него. Хорошо спроектированное распределённое приложение может освобождать вычислительные ресурсы путём отключения ненужных узлов или служб.

Производительность (performance)
Распределённое приложение также может обеспечить уникальные преимущества производительности по сравнению с монолитным приложением:

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

  • Распределённое приложение может обеспечить снижение задержки при обработке запросов из определённых регионов с помощью служб, расположенных в том же регионе. В отличие от монолитного приложения, которое может давать низкую задержку запросов только в том регионе, где оно физически находится.
Возвращение транзакционной согласованности
В начальные годы существования «современного» интернета Web 2.0 архитекторы приложений были вынуждены выбирать между доступностью, глобальной масштабируемостью и согласованностью — и было широко распространено мнение, что из трех этих явно желательных качеств нужно выбирать два (см. CAP-теоремаприм.ред.). В результате, от сильной согласованности отказались в пользу «согласованности в конечном счёте» (eventual consistency), а значение «конечного счёта» было растянуто до «возможно, никогда».

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

Экономика распределённых систем начала революционно меняться за счёт технологических достижений за последние 10-15 лет:

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

  • Технологии контейнеризации, прежде всего Docker, вместе с паттернами проектирования микросервисов, могут быть использованы для создания легко развёртываемых и воспроизводимых элементов функциональности приложений.

  • Решения для оркестрации контейнеров, особенно Kubernetes, позволяют разворачивать и масштабировать контейнеры, из которых собирается приложение, простым и надёжным способом.

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

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

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

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

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

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

  • Применение паттерна микросервисов для верхнеуровневой архитектуры приложения

  • Использование Docker контейнеров для упаковки микросервисов

  • Там где можно, использование Kubernetes для оркестрации развёртывания и поддержки этих контейнеров

  • Факультативное развёртывание слоя обмена сообщениями, такого как Kafka, для взаимодействия между сервисами

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

Давайте кратко рассмотрим каждый из упомянутых элементов современной распределённой архитектуры:

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

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

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

Оркестрация контейнеров и Kubernetes
Системы оркестровки контейнеров, такие как Kubernetes, организуют развёртывание и управление контейнерами, из которых состоит приложение. Kubernetes создаёт и поддерживает контейнеры, составляющие приложение, а также управляет их избыточностью, масштабируемостью и надёжностью (resilience).
Распределённые базы данных
Использование контейнеризованных микросервисов, оркестрируемых Kubernetes, хорошо подходит для логики приложений. Однако слой хранения данных — база данных — имеет другой набор требований.

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

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

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

Такие базы данных не существовали до недавнего времени, но сегодня они доступны. Некоторые из данных кандидатов, такие как CockroachDB и YugabyteDB доступны, как для локального развёртывания, так и в виде полностью управляемых облачных систем. Другие, такие как Google Spanner или Microsoft Azure Cosmos DB, доступны только на определённой облачной платформе.
Подводим итоги
Современным предприятиям требуются высокодоступные, глобально масштабируемые программные решения с поддержкой распределённых транзакций. Эти требования наилучшим образом удовлетворяются распределёнными транзакционными архитектурами приложений.

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

В главе 2 мы более подробно рассмотрим архитектуру слоя приложений, а в главе 3 мы исследуем распределённый слой базы данных.