📜 25 лет спустя: манифест распределенных вычислений Amazon 1998 года

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

Данная статья является переводом. Оригинал статьи доступен по ссылке.

Информация об архитектуре электронной коммерции Amazon редко становилась доступной общественности. Поэтому, когда в 2004 году Amazon пригласил меня выступить с докладом о моих исследованиях в области распределенных систем, я почти отказался. Я думал: «Неужели веб‐серверы и база данных – это так сложно?». Но я рад, что поехал, потому что то, с чем я столкнулся, взорвало мне мозг. Масштаб и разнообразие их работы не были похожи ни на что, что я когда‐либо видел, архитектура Amazon была, по крайней мере, на десятилетие впереди того, с чем я сталкивался в других компаниях. Это был не просто высокопроизводительный веб‐сайт, мы говорим обо всем: от обработки большого количества транзакций до машинного обучения, безопасности, робототехники, сортировки миллионов товаров – все, что можно найти в учебнике по распределенным системам, встречалось в Amazon, и происходило это в невероятных масштабах. Когда они предложили мне работу, я не смог устоять. Сейчас, спустя почти 18 лет работы в должности технического директора, я по‐прежнему ежедневно поражаюсь изобретательности наших инженеров и созданным ими системам.

Изобретать и упрощать

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

В течение следующих двух десятилетий Amazon перешла от монолита к service‐oriented architecture (сервис‐ориентированной архитектуре), затем к микросервисам, затем к микросервисам, работающим на платформе с общей инфраструктурой. Все это делалось еще до появления термина service‐oriented architecture. В процессе мы узнали многое о работе в масштабах интернета.

Через пару недель я планирую выступить на AWS re:Invent и рассказать о том, как концепции из манифеста повлияли на формирование микросервисов и событийно‐ориентированных архитектур. Кроме того, в ближайшие месяцы я напишу серию постов, посвященных конкретным разделам «Манифеста распределенных вычислений».

Очень краткая история системной архитектуры на Amazon

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

Amazon начинал с традиционной двухуровневой архитектуры: монолитное приложение без статических данных (Obidos), которое использовалось для обслуживания страниц, и целый комплекс баз данных, который рос с каждым новым набором категорий товаров, товаров внутри этих категорий, клиентов и стран, в которых запускался Amazon. Эти базы данных были общим ресурсом и, в конечном итоге, стали «узким местом», помешав внедрению инноваций из‐за снижения темпов.

Еще в 1998 году группа старших инженеров Amazon начала закладывать основу для радикальной перестройки архитектуры Amazon, чтобы поддержать следующее поколение инновационных продуктов, ориентированных на клиента. Основным пунктом было разделение представительного уровня, бизнес‐логики и данных, при этом надежность, масштаб, производительность и безопасность должны были соответствовать невероятно высокой планке, а затраты должны были оставаться под контролем. Их предложение получило название «Манифест распределенных вычислений».

Я рассказываю об этом сейчас, чтобы дать вам представление о том, насколько передовым было мышление инженерной команды Amazon в конце девяностых годов. Они последовательно изобретали, решали проблемы, масштабируя монолит в то, что сейчас мы бы назвали service‐oriented architecture, что было необходимо для поддержки быстрого внедрения инноваций, ставших синонимом Amazon. Один из наших принципов руководства – изобретать и упрощать. Наши инженеры действительно живут с этим девизом.

Все меняется...

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

Полный текст «Манифеста распределенных вычислений» доступен ниже. Вы также можете просмотреть его в формате PDF.

Манифест распределенных вычислений

Создано: 24 мая 1998 года

Пересмотрено: 10 июля 1998 года

Предыстория

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

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

Ключевые понятия

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

Service‐based model

Мы предлагаем перейти к трехуровневой архитектуре, где представление (клиент), бизнес‐логика и данные разделены. Это и есть service‐based architecture. Приложения (клиенты) больше не смогут обращаться к базе данных напрямую, а только через четко определенный интерфейс, который инкапсулирует бизнес‐логику, необходимую для выполнения функции. Это означает, что клиент больше не зависит от базовой структуры данных или даже от того, где эти данные находятся. Интерфейс между бизнес‐логикой (в сервисе) и базой данных может меняться без влияния на клиента, поскольку клиент взаимодействует с сервисом через свой собственный интерфейс. Аналогично, клиентский интерфейс может меняться, не влияя на взаимодействие сервиса с базой данных.

Службы в сочетании с рабочим процессом должны будут предоставлять как синхронные, так и асинхронные методы. Синхронные методы, скорее всего, будут применяться к операциям, для которых ответ необходим немедленно, например, добавление клиента или поиск информации о поставщике. Однако другие операции, которые являются асинхронными по своей природе, не обеспечат немедленного ответа. Примером может служить вызов службы для передачи элемента рабочего процесса на следующий узел обработки в цепочке. Запрашивающий не ожидает немедленного возврата результатов, а только указание на то, что элемент рабочего процесса был успешно поставлен в очередь. Однако реквестор (клиент сервера) может быть заинтересован в получении результатов запроса в конечном итоге. Чтобы облегчить работу, служба должна предоставить механизм, с помощью которого запрашивающая сторона может получить результаты асинхронного запроса. Для этого подойдут модели polling или callback. В случае callback, реквестор передает адрес процедуры, которая должна быть вызвана после выполнения запроса. Этот подход чаще всего используется, когда время между запросом и ответом относительно невелико. Существенным недостатком модели callback является то, что после завершения запроса реквестор может быть уже не активен, что делает адрес callback недействительным. Модель polling, однако, страдает от накладных расходов, необходимых для периодической проверки выполнения запроса. Модель polling, вероятно, будет наиболее полезной для взаимодействия с асинхронными службами.

Есть несколько важных последствий, которые необходимо учитывать при переходе к модели service‐based.

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

Вторым следствием service‐based подхода, который связан с первым, является значительное изменение мышления разработчиков программного обеспечения. Наше нынешнее мышление ориентировано на данные, и когда мы моделируем бизнес‐требования, мы используем data‐centric подход. Наши решения задач включают в себя изменения таблиц или столбцов базы данных, и мы встраиваем модель данных в приложение для доступа. Service‐based подход потребует от нас разбить решение бизнес‐требований как минимум на две части. Первая часть – это моделирование отношений между элементами данных, как мы это делали всегда. Сюда входит модель данных и бизнес‐правила, которые будут применяться в сервисе (сервисах), взаимодействующим с данными. Однако вторая часть – это то, чего мы никогда не делали раньше, а именно разработка интерфейса между клиентом и сервисом таким образом, чтобы базовая модель данных не была открыта для клиента и не зависела от него. Это в значительной степени связано с вопросами программной инженерии, о которых говорилось выше.

Модель потока операций (Workflow‐based Model) и Data Domaining

Бизнес Amazon хорошо подходит для модели потока операций. У нас уже есть «конвейер заказов», который обрабатывается различными бизнес‐процессами с момента размещения заказа клиента до момента его отправки. Большая часть нашей обработки уже ориентирована на поток операций (или рабочего процесса), хотя его «элементы» статичны и находятся в основном в одной базе данных. Примером нашей текущей модели рабочего процесса является путь customer_orders через систему. Атрибут условия каждого customer_orders диктует следующее действие в рабочем процессе. Однако текущая модель рабочего процесса в базе данных не будет хорошо масштабироваться, поскольку обработка выполняется на исполняющейся копии программы. По мере увеличения объема работы (большее количество заказов в единицу времени), объем обработки будет возрастать до потери устойчивости. Решением этой проблемы является распределение обработки рабочего процесса таким образом, чтобы она могла быть разгружена от исполняющейся копии программы. Реализация этого требует, чтобы элементы рабочего процесса, такие как customer_orders, перемещались между бизнес‐обработкой («узлами»), которые могут быть расположены на отдельных машинах. Вместо того чтобы процессы приходили к данным, данные должны были перемещаться к процессам. Это означает, что каждый элемент рабочего процесса требует всей информации, необходимой для того, чтобы следующий узел рабочего процесса мог действовать в соответствии с ним. Эта концепция аналогична той, которая используется в ориентированном на сообщения промежуточном ПО, где единицы работы представлены в виде сообщений, передаваемых от одного узла (бизнес‐процесса) к другому.

Проблема рабочего процесса заключается в том, как что направляется. Имеет ли каждый узел обработки самостоятельность в перенаправлении элемента рабочего процесса на следующий узел на основе встроенных бизнес‐правил (автономный) или должен быть некий координатор рабочего процесса, который управляет передачей работы между узлами (направленный)? Чтобы проиллюстрировать разницу, рассмотрим узел, который осуществляет списание средств с кредитной карты. Имеет ли он встроенный «интеллект» для передачи заказов, которые были успешными, на следующий узел обработки в конвейере заказов и передачи заказов, которые не были выполнены, на какой‐то другой узел для обработки исключений? Или узел списания средств с кредитной карты рассматривается как служба, которая может быть вызвана из любого места и которая возвращает свои результаты запрашивающему? В этом случае реквестер будет отвечать за обработку условий отказа и определение следующего узла обработки для успешных и неудачных запросов. Основным преимуществом модели направленного рабочего процесса является ее гибкость. Узлы обработки рабочего процесса, между которыми перемещается работа, представляют собой взаимозаменяемые строительные блоки, которые можно использовать в различных комбинациях и для различных целей. Некоторые виды обработки очень хорошо поддаются направленной модели (directed model), например, обработка платежей по кредитным картам, поскольку их можно вызывать в различных контекстах. В более широком масштабе DC‐обработка, рассматриваемая как единый логический процесс, выигрывает от использования направленной модели. DC будет принимать заказы клиентов для обработки и возвращать результаты (отгрузка, условия исключения и т. д.) тому, кто дал ему задание на выполнение. С другой стороны, некоторые процессы выиграют от автономной модели, если их взаимодействие со смежными процессами фиксировано и вряд ли изменится. Примером может служить то, что многокнижные отправления всегда проходят путь от выпадающего списка до ребина.

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

Однако у модели распределенных рабочих процессов, основанной на обмене сообщениями (message‐based), есть некоторые недостатки. Модель, ориентированная на базу данных (database‐centric), где каждый процесс обращается к одному и тому же центральному хранилищу данных, позволяет быстро и эффективно распространять изменения данных в системе. Например, если клиент хочет изменить номер кредитной карты, используемой для заказа, потому что срок действия указанной им изначально кредитной карты истек или она была отклонена, изменение можно легко внести, и оно будет мгновенно представлено в системе. В модели рабочего процесса, основанной на обмене сообщениями, это становится сложнее. При проектировании рабочего процесса нужно учитывать тот факт, что некоторые базовые данные могут измениться, пока элемент рабочего процесса проходит путь от одного конца системы к другому. Кроме того, при классическом рабочем процессе на основе очередей сложнее определить состояние любого конкретного элемента рабочего процесса. Чтобы решить данную проблему, необходимо создать механизмы, позволяющие записывать переходы состояния в интересах внешних процессов без ущерба для доступности и автономности рабочего процесса. Таким образом, правильное первоначальное проектирование становится гораздо более важным, чем в случае с монолитной системой, и говорят о практике разработки программного обеспечения, которая обсуждалась в других разделах.

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

Применение концепций

DC‐обработка хорошо подходит в качестве примера применения концепций рабочего процесса и data domaining, рассмотренных выше. Существует 3 различные категории DC‐обработки.

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

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

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

Отслеживание изменений состояния

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

Внесение изменений в элементы рабочего процесса

Обработка рабочего процесса создает проблему целостности данных, поскольку элементы рабочего процесса содержат всю информацию, необходимую для перехода к следующему узлу рабочего процесса. Что делать, если клиент хочет изменить адрес доставки заказа, пока заказ находится в процессе обработки? В настоящее время представитель CS может изменить адрес доставки в customer_order (при условии, что это произойдет до создания pending_customer_shipment), поскольку и заказ, и данные клиента находятся в центре. Однако в модели потока операций заказ клиента будет находиться в другом месте, проходя различные этапы обработки на пути к тому, чтобы отправиться к клиенту. Чтобы повлиять на изменение элемента рабочего процесса, должен существовать механизм распространения изменений атрибутов. Модель публикации и подписки является одним из методов. Для реализации модели P&S узлы обработки рабочего процесса должны подписаться на получение уведомлений об определенных событиях или исключениях. Изменения атрибутов представляют собой один из классов событий. Чтобы изменить адрес для бортового заказа, сообщение с указанием заказа и измененного атрибута будет отправлено всем узлам обработки, которые подписались на это конкретное событие. Кроме того, в таблицу отслеживания будет вставлена строка изменения состояния, указывающая на то, что был запрошен запрос на изменение атрибута. Если один из узлов сможет повлиять на изменение атрибута, он вставит еще одну строку в таблицу изменения состояния, чтобы указать, что он внес изменение в заказ. Этот механизм означает, что будет существовать постоянная запись событий изменения атрибутов и того, были ли они применены. Другим вариантом модели P&S является модель, в которой координатор рабочего процесса вместо узла обработки рабочего процесса влияет на изменения элементов в ходе рабочего процесса. Как и в описанном выше механизме, координаторы рабочего процесса подписываются на получение уведомлений о событиях или исключениях и применяют их к соответствующим элементам рабочего процесса по мере их обработки. Синхронное применение изменений к текущим элементам рабочего процесса является альтернативой асинхронному распространению запросов на изменение. Преимущество этой модели заключается в том, что создатель запроса на изменение получает мгновенную обратную связь о том, было ли затронуто изменение или нет. Однако эта модель требует, чтобы все узлы рабочего процесса были доступны для синхронной обработки изменений, и должна использоваться только для изменений в том случае, когда допустимо, чтобы запрос не прошел из‐за временной недоступности.

Рабочий процесс и DC‐обработка заказов клиентов

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

***
Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека программиста»

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ