Я присоединился к Uber два года назад в качестве мобильного разработчика с некоторым опытом серверной разработки.
Здесь я разрабатывал платежный функционал в приложении — и попутно переписал само приложение .
После чего я перешел в управление развитием и руководил самой командой.
Это позволило мне лучше познакомиться с серверной частью, поскольку моя команда отвечает за многие из наших серверных систем, которые позволяют осуществлять платежи.
До работы в Uber у меня не было опыта работы с распределенными системами.
Я получил традиционное образование в области компьютерных наук, после чего десять лет работал в сфере Full-Stack разработки.
Так что пока я мог рисовать различные диаграммы и говорить о компромиссах ( компромиссы ) в системах я к тому времени недостаточно хорошо понимал и воспринимал такие понятия распределения, как непротиворечивость( последовательность ), доступность ( доступность ) или идемпотентность ( идемпотентность ).
В этом посте я расскажу о некоторых концепциях, которые мне нужно было изучить и применить на практике при создании крупномасштабной, высокодоступной, распределенной платежной системы, которую Uber использует сегодня.
Это система с нагрузкой до нескольких тысяч запросов в секунду, в которой критический платежный функционал должен работать корректно даже в тех случаях, когда отдельные части системы перестают работать.
Это полный список? Скорее всего нет. Однако если бы я лично узнал об этих понятиях раньше, это значительно облегчило бы мою жизнь.
Итак, давайте начнем наше погружение в SLA, согласованность, надежность данных, устойчивость сообщений, идемпотентность и некоторые другие вещи, которые мне нужно было изучить на моей новой работе.
Соглашение об уровне обслуживания
В больших системах, обрабатывающих миллионы событий в день, что-то обязательно пойдет не так.Вот почему, прежде чем мы углубимся в системное планирование, самый важный шаг, который нам необходимо сделать, — это решить, что для нас значит «здоровая» система.
Степень «здоровья» должна быть такой, Фактически можно измерить.
Общепринятым способом измерения работоспособности системы является Соглашение об уровне обслуживания ( соглашения об уровне обслуживания ).
Вот некоторые из наиболее распространенных типов SLA, с которыми я столкнулся на практике:
- Доступность : процент времени, в течение которого служба работает. Хотя достижение 100-процентной доступности может показаться заманчивым, достижение этого результата может быть действительно сложной и к тому же весьма дорогостоящей задачей.
Даже крупные и критически важные системы, такие как сети карт VISA, Gmail или интернет-провайдеры, не имеют 100% доступности — с годами они будут накапливать секунды, минуты или часы, проведенные в простое.
Для многих систем доступность равна четырем девяткам (99,99% или примерно 50 минут простоя в год ) считается высокой доступностью.
Чтобы достичь этого уровня, вам придется много работать.
- Точность : Допустима ли потеря или неточность данных? Если да, то какой процент приемлем? Для платежной системы, над которой я работал, этот показатель должен был составлять 100%, поскольку не было возможности потерять данные.
- Пропускная способность/мощность (емкость) : Какую нагрузку должна выдерживать система? Эта метрика обычно выражается в запросах в секунду.
- Задержка : Как долго система должна реагировать? Сколько времени должно занять обслуживание 95% и 99% запросов? В таких системах обычно многие запросы являются «шумовыми», поэтому задержки p95 и p99 найти более практическое применение в реальном мире.
Чтобы убедиться, что мы все делаем правильно и что наша новая система будет «лучше», чем ее предшественница, мы использовали соглашение об уровне обслуживания, чтобы определить наши ожидания от нее.
Доступность была одним из важнейших требований.
После того как мы определили цель, нам нужно было понять компромиссы в архитектуре для достижения этих целей.
Горизонтальное и вертикальное масштабирование
По мере роста бизнеса, использующего нашу вновь созданную систему, нагрузка на него будет только увеличиваться.В определенный момент существующая установка не сможет выдерживать дальнейшее увеличение нагрузки, и нам придется увеличить грузоподъемность.
Двумя распространенными стратегиями масштабирования являются вертикальное масштабирование и горизонтальное масштабирование.
Горизонтальное масштабирование подразумевает добавление в систему большего количества машин (или узлов) для увеличения пропускной способности ( емкость ).
Горизонтальное масштабирование — самый популярный способ масштабирования распределенных систем.
Вертикальное масштабирование, по сути, означает «купить более крупную/более мощную машину» — (виртуальную) машину с большим количеством ядер, большей вычислительной мощностью и большим объемом памяти.
В случае распределенных систем вертикальное масштабирование обычно менее популярно, поскольку оно может быть более дорогим, чем горизонтальное масштабирование.
Однако некоторые известные крупные сайты, такие как Stack Overflow, успешно масштабируется по вертикали чтобы соответствовать нагрузке.
Почему стратегия масштабирования имеет смысл при создании крупной платежной системы? С самого начала мы решили, что построим систему, которая будет масштабироваться горизонтально.
Хотя в некоторых случаях вертикальное масштабирование приемлемо, наша платежная система уже достигла запланированной нагрузки, и мы пессимистично относились к предположению, что один сверхдорогой мэйнфрейм сможет справиться с этой нагрузкой сегодня, не говоря уже о будущем.
.
Кроме того, в нашей команде было несколько человек, которые работали на крупных поставщиков платежных услуг и имели негативный опыт попыток вертикального масштабирования даже на самых мощных машинах, которые можно было купить за деньги в те годы.
Последовательность
Наличие любой системы имеет важное значение.Распределенные системы часто состоят из машин, чья индивидуальная доступность ниже, чем доступность всей системы.
Пусть наша цель — построить систему с доступностью 99,999% (время простоя примерно 5 минут в год).
Мы используем машины/узлы со средней доступностью 99,9% (они простаивают примерно 8 часов в год).
Прямой путь достижения нужного нам показателя доступности — добавить в кластер еще несколько таких машин/узлов.
Даже если некоторые узлы выйдут из строя, другие продолжат работать, а общая доступность системы будет выше доступности ее отдельных компонентов.
Согласованность — ключевой вопрос в системах высокой доступности.
Система является согласованной, если все узлы видят и возвращают одни и те же данные одновременно.
В отличие от нашей предыдущей модели, в которой мы добавили больше узлов для достижения большей доступности, обеспечение согласованности системы не так тривиально.
Чтобы гарантировать, что каждый узел содержит одинаковую информацию, они должны отправлять друг другу сообщения, чтобы всегда оставаться синхронизированными.
Однако сообщения, которые они отправляют друг другу, могут не быть доставлены — они могут быть потеряны, а некоторые узлы могут оказаться недоступными.
Последовательность — это концепция, на понимание и оценку которой мне потребовалось больше всего времени.
Существует несколько типов консистенции , наиболее широко используемым в распределенных системах является сильная консистенция ( сильная консистенция ), слабая консистенция ( слабая консистенция ) И предельная согласованность ( конечная согласованность ).
Вы можете прочитать полезный практический анализ преимуществ и недостатков каждой модели.
Как правило, чем слабее требуемый уровень согласованности, тем быстрее может работать система, но тем больше вероятность того, что она не вернет последний набор данных.
Почему при построении крупной платежной системы стоит учитывать последовательность? Данные в системе должны быть согласованными.
Но насколько они последовательны? Для некоторых частей системы подойдут только высокосогласованные данные.
Например, нам необходимо хранить в строго согласованной форме информацию о том, что платеж был инициирован.
Для других частей системы, которые не столь важны, согласованность в конечном итоге можно считать разумным компромиссом.
Это хорошо иллюстрируется перечислением последних транзакций: их можно реализовать с использованием событийной согласованности ( конечная согласованность ) — то есть последняя транзакция может появиться в некоторых частях системы только через некоторое время, но это заставит запрос списка возвращать результат с меньшей задержкой или требовать меньше ресурсов для выполнения.
Долговечность данных
Долговечность означает, что как только данные будут успешно добавлены в хранилище данных, они будут доступны нам в будущем.Это будет верно, даже если узлы системы перейдут в автономный режим, выйдут из строя или данные узла будут повреждены.
Различные распределенные базы данных имеют разные уровни надежности данных.
Некоторые из них поддерживают долговечность данных на уровне машины/узла, другие делают это на уровне кластера, а некоторые вообще не предоставляют эту функциональность «из коробки».
Для повышения надежности обычно используется та или иная форма репликации: если данные хранятся на нескольких узлах и один из узлов выходит из строя, данные все равно будут доступны.
Вот хорошая статья , что объясняет, почему достижение долговечности в распределенных системах может оказаться серьезной проблемой.
Почему надежность данных имеет значение при построении платежной системы? Если данные критически важны (например, платежи), мы не можем позволить себе потерять их во многих частях нашей системы.
Созданные нами распределенные хранилища данных должны были обеспечивать надежность данных на уровне кластера, чтобы даже в случае сбоя экземпляров завершенные транзакции сохранялись.
В наши дни большинство служб распределенного хранения, таких как Cassandra, MongoDB, HDFS или Dynamodb, поддерживают надежность на различных уровнях и могут быть настроены для обеспечения надежности на уровне кластера.
Постоянство и долговечность сообщений
Узлы в распределенных системах выполняют вычисления, хранят данные и отправляют друг другу сообщения.Ключевой характеристикой отправки сообщений является надежность их доставки.
Для критически важных систем часто требуется, чтобы никакие сообщения не терялись.
В случае распределенных систем обмен сообщениями ( обмен сообщениями ) обычно выполняется с использованием какого-либо сервиса распределенных сообщений — RabbitMQ, Kafka или других.
Эти брокеры сообщений могут поддерживать (или настроены для поддержки) различные уровни надежности доставки сообщений.
Сохраняемость сообщения означает, что в случае сбоя узла, обрабатывающего сообщение, сообщение все равно будет доступно для обработки после устранения проблемы.
Долговечность сообщения обычно используется на уровне очереди сообщений .
При использовании устойчивой очереди сообщений, если очередь (или узел) отключается при отправке сообщения, он все равно получит сообщение, когда вернется в режим онлайн.
Хорошая подробная статья по этому вопросу доступен по ссылке .
Почему безопасность и долговечность сообщений так важны при построении крупных платежных систем? У нас были сообщения, которые мы не могли себе позволить потерять, например сообщение о том, что человек инициировал оплату за поездку.
Это означало, что система обмена сообщениями, которую мы будем использовать, должна была работать без потерь: каждое сообщение должно было быть доставлено один раз.
Однако создание системы, которая доставляет каждое сообщение гладкий один раз, а не по меньшей мере один раз – это задания, существенно различающиеся по сложности.
Мы решили реализовать систему обмена сообщениями, которая доставляет хотя бы один раз, и выбрали шину сообщений ( шина обмена сообщениями ), поверх которого мы решили его построить (выбрали Kafka, создав lossless-кластер, что и требовалось в нашем случае).
Идемпотентность
В случае с распределенными системами все может пойти не так — соединения могут обрушиться посередине или время выполнения запросов может истечь.Клиенты будут часто повторять эти запросы.
Идемпотентная система гарантирует, что независимо от того, что произойдет и сколько раз будет выполнен конкретный запрос, фактическое выполнение этого запроса произойдет только один раз.
Хорошим примером является осуществление платежа.
Если клиент создает запрос на оплату, запрос считается успешным, но если время ожидания клиента истекло, клиент может повторить тот же запрос.
В случае идемпотентной системы с лица, осуществляющего платеж, не будет взиматься плата дважды; но для неидемпонетной системы это вполне возможное явление.
Проектирование идемпотентных распределенных систем требует некоторой стратегии распределенной блокировки.
Именно здесь в игру вступают концепции, которые мы обсуждали ранее.
Допустим, мы намерены реализовать идемпотентность, используя оптимистическую блокировку, чтобы избежать одновременных обновлений.
Чтобы мы могли использовать оптимистическую блокировку, система должна быть строго согласованной — чтобы во время выполнения операции мы могли проверить, началась ли другая операция, используя ту или иную форму управления версиями.
Существует множество способов достижения идемпотентности, и каждый конкретный выбор будет зависеть от ограничений системы и типа выполняемой операции.
Разработка идемпотентных подходов — достойная задача для разработчика — достаточно посмотреть посты Бен Надель, в котором он рассказывает о различных стратегиях, которые он использовал.
, которые включают в себя как распределенные блокировки, так и ограничения ( ограничения ) База данных.
Когда вы проектируете распределенную систему, идемпотентность может оказаться одной из частей, которую вы упускаете из виду.
В нашей практике мы сталкивались со случаями, когда моя команда обжигалась, не проверив наличие правильной идемпотентности для некоторых ключевых операций.
Почему идемпотентность важна при построении крупной платежной системы? Самое главное: избежать двойного дебетования и двойного возврата средств.
Учитывая, что наша система обмена сообщениями осуществляет хотя бы одну доставку без потерь, мы должны предположить, что все сообщения могут быть доставлены несколько раз, и системы должны гарантировать идемпотентность.
Мы решили решить эту проблему с помощью управления версиями и оптимистической блокировки, когда наши системы реализуют идемпотентное поведение, используя строго согласованное хранилище в качестве источника данных.
Шардинг и кворум
Распределенным системам часто необходимо хранить гораздо больше данных, чем может обработать один узел.Так как же нам хранить набор данных на нужном количестве машин? Самая популярная техника для этого шардинг .
Данные секционируются горизонтально с использованием хеша, присвоенного разделу.
Хотя многие распределенные базы данных сегодня реализуют сегментирование «под капотом», это интересная тема сама по себе, которую стоит изучить, особенно перешардинг .
В 2010 году у Foursquare случился 17-часовой простой из-за крайнего случая шардинга, после чего компания поделилась интересное вскрытие , проливая свет на корень проблемы.
Во многих распределенных системах данные или вычисления реплицируются на нескольких узлах.
Чтобы гарантировать последовательное выполнение операций, определен подход голосования, при котором определенное количество узлов должно иметь одинаковый результат, чтобы операция считалась успешной.
Этот процесс называется кворумом.
Почему кворум и шардинг имеют смысл при построении крупной платежной системы в Uber? Оба эти понятия просты и используются практически повсеместно.
Я познакомился с ними, когда мы настраивали репликацию в Кассандре.
Кассандра (и другие распределенные системы) использует кворум и местный кворум ( местный кворум ), чтобы обеспечить согласованность между кластерами.
Модель актера
Обычный словарный запас, который мы используем для описания методов программирования (такие вещи, как переменные, интерфейсы, вызовы методов), предполагает одномашинные системы.Когда мы говорим о распределенных системах, мы должны использовать разные подходы.
Распространенным способом описания таких систем является модель актера , внутри которого мы видим код с точки зрения коммуникации.
Эта модель популярна, поскольку совпадает с ментальной моделью того, как мы представляем себе, например, взаимодействие людей в организации.
Другой, не менее популярный способ описания распределенных систем — CSP. взаимодействующие последовательные процессы .
Модель актеров основана на актерах, которые отправляют сообщения друг другу и реагируют на них.
Каждый актор может делать ограниченный набор действий — создавать других акторов, отправлять сообщения другим или решать, что делать со следующим сообщением.
С помощью нескольких простых правил мы можем довольно хорошо описать сложные распределенные системы, которые могут восстановиться после сбоя актора.
Если вы не знакомы с таким подходом, то рекомендую вам статью Модель актера за 10 минут к Брайан Сторти .
Для многих языков существуют библиотеки или фреймворки, реализующие модель актера .
Например, в Uber мы используем Акка для некоторых наших систем.
Почему имеет смысл применять модель актера к крупной платежной системе? В разработке нашей системы принимало участие множество инженеров, большинство из которых уже имели опыт работы с распределенными системами.
Мы решили следовать стандартной распределенной модели, а не прыгать и изобретать собственные принципы распределенного управления.
Реактивная архитектура
При построении больших распределенных систем обычно цель состоит в том, чтобы сделать их отказоустойчивыми, эластичными и масштабируемыми.Будь то платежная система или какая-то другая высоконагруженная система, шаблоны достижения желаемого могут быть одинаковыми.
Те, кто занимается такими системами, регулярно открывают и распространяют передовой опыт их создания, а реактивная архитектура — аналогичный популярный и широко используемый шаблон.
Для введения в реактивную архитектуру я предлагаю прочитать Реактивный манифест ( на русском ) и посмотрите 12-минутное видео связь .
Почему реактивная архитектура имеет смысл, если вы создаете крупную платежную систему? Акка , библиотека, которую мы использовали для создания большей части нашей новой платежной системы, находится под сильным влиянием реактивной архитектуры.
Многие из наших инженеров, создавших эту систему, уже были знакомы с лучшими практиками реактивного программирования.
Следование реактивным принципам – создание отзывчивой, отказоустойчивой, эластичной системы, управляемой сообщениями ( управляемый сообщениями ), мы пришли к такому выводу естественным образом.
Наличие модели, на которую я могу положиться для оценки прогресса и направления разработки, оказалось чрезвычайно полезным, и я буду полагаться на нее в будущем при создании новых систем.
Заключение
Мне посчастливилось участвовать в реинжиниринге крупномасштабной, распределенной и критически важной системы: той, которая обеспечивает платежи в Uber. Работа в этой среде открыла мне множество распределенных концепций, которые я никогда раньше не использовал.Я собрал их здесь в надежде, что мой рассказ другим будет полезен для начала изучения распределенных систем или почерпнет для себя что-то новое.
Этот пост был посвящен исключительно планированию и архитектуре таких систем.
Есть много разных тонкостей, о которых стоит поговорить – о построении, развертывании и миграции высоконагруженных систем – а также об их надежной работе; Все эти темы я собираюсь поднять в последующих постах.
Теги: #Uber #высоконагруженные проекты #проектирование систем #Распределенные системы #Высокая производительность #Анализ и проектирование систем
-
Восстановите Реестр Windows Мгновенно
19 Oct, 24 -
Как Получить Любой Символ В Gnome
19 Oct, 24 -
Skype 2.1.0.81 Бета Для Linux
19 Oct, 24 -
Pocketpc: Новая Версия Icq-Клиента Mchat
19 Oct, 24