Архитектура Сервиса Распределенной Очереди Сообщений В Яндекс.облаке

Здравствуйте, меня зовут Василий Богонатов.

Я один из тех, кто приложил руку и голову и вложил душу в сервис распределенных постоянных очередей сообщений Yandex Message Queue. Сервис стал общедоступным в конце мая, но внутри Яндекса он уже давно активно используется в различных продуктах.

Сегодня я хочу рассказать читателям Хабра об очередях сообщений вообще и Очереди сообщений Яндекса в частности.

Сначала я хочу объяснить, что такое «распределенная постоянная очередь сообщений» и зачем она нужна.

Покажем его практическую ценность, механику работы с сообщениями, поговорим об API и простоте использования.

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



Архитектура сервиса распределенной очереди сообщений в Яндекс.
</p><p>
Облаке



Что такое распределенная постоянная очередь сообщений?

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

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

Немного уточним термины.

Очередь сообщений — это хранилище, обеспечивающее размещение и чтение данных в определенном порядке.

С очередью обычно взаимодействуют два типа сущностей:

  • сценаристы (продюсеры) – отправлять сообщения в очередь;
  • читатели – получать (читать) сообщения из очереди.

При использовании очереди читатели и писатели не зависят друг от друга.

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

Основной сценарий работы очереди — надежная и быстрая передача сообщений от автора к читателю.

В отличие от базы данных очередь не предназначена для длительного хранения сообщений.

Во многих популярных реализациях есть соответствующий параметр – «Срок хранения сообщений».

Он определяет, как долго будет храниться сообщение, прежде чем оно будет окончательно удалено.

С понятием очереди мы разобрались, перейдем к «распределению» и «постоянству».

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

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

Распределение и сохранение не влияют на основную функциональность очереди; они обеспечивают отказоустойчивость и надежность хранения данных.

Какие типы сбоев могут произойти в нашей системе, мы рассмотрим чуть позже.

Однако не могу отказать себе в удовольствии и немного раскрою карты: за всю историю сервиса мы не потеряли ни одного сохраненного клиентского сообщения.



Для чего нужна очередь сообщений?

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

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

Очереди сглаживают пики нагрузки: они выполняют роль буфера для читателей.

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

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

Давайте посмотрим, как это работает на примере поисковый робот (ведь Яндекс начинал с поиска!), который загружает, обрабатывает и помещает веб-страницы в базу данных.

Возьмем эту архитектуру.



Архитектура сервиса распределенной очереди сообщений в Яндекс.
</p><p>
Облаке

Очередь сообщений решает здесь следующие проблемы:

  1. Робот работает гораздо быстрее, чем воркеры, отвечающие за парсинг и загрузку страниц в базу данных.

    Без очереди ссылки накапливались бы и заполняли доступную память или диск.

    То же самое произошло бы, если бы работники были временно недоступны.

  2. Без очереди роботу необходимо «знать» интерфейс рабочих, чтобы назначать им задачи.

    Интерфейс может меняться по мере развития продукта.

  3. Отдельный воркер имеет достаточно низкую надежность, поэтому нет никакой гарантии, что передаваемая ссылка будет обработана им полностью.

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

Если один работник выйдет из строя, необработанная ссылка будет возвращена в очередь через определенный период времени для обработки другим исполнителем.

Очередь имеет собственный интерфейс, который протестирован и описан в документации, благодаря чему краулер и рабочие системы могут разрабатывать разные команды на разных языках программирования.

Это не повлияет на общую производительность.



Как Яндекс Очередь сообщений работает с сообщениями

Здесь можно выделить три основных этапа:
  • записывать сообщения в очередь;
  • чтение сообщения из очереди;
  • удаление сообщения из очереди.

Запись считается успешной, если сообщение было надежно сохранено и в ближайшее время станет доступно читателям.

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

Когда сообщение прочитано, оно скрывается из очереди на период времени, называемый тайм-аутом видимости, и становится недоступным для других читателей.

Если таймаут видимости истекает, сообщение возвращается в очередь и снова становится доступным для обработки.

Порядок чтения сообщений определяется очередью, а не программой чтения.

Сам ридер и сетевое подключение к нему потенциально ненадежны.

Таймаут видимости необходим для того, чтобы иметь возможность вернуть сообщение в очередь в случае сбоя ридера или потери соединения.

В противном случае существует вероятность того, что отдельное сообщение никогда не будет обработано корректно.

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



Виды очередей в Яндекс Очереди сообщений

Первый и наиболее часто используемый тип — стандартная очередь.

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

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



Архитектура сервиса распределенной очереди сообщений в Яндекс.
</p><p>
Облаке

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

Из-за использования шардинга запрос на чтение может не вернуть никаких сообщений, даже если они находятся в очереди.

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

Второй тип - ФИФО – противоположность стандартной очереди.

Обеспечивает строгий порядок чтения, поддерживает дедупликацию записи и повторные попытки сообщений.

Производительность и масштабируемость ниже стандартных.

Производительность очереди FIFO ограничена 30 запросами в секунду.

FIFO рекомендуется, когда вам нужно попытаться обеспечить семантику доставки «ровно один раз».

Обычно слово «очередь» означает FIFO.

Архитектура сервиса распределенной очереди сообщений в Яндекс.
</p><p>
Облаке



API очереди сообщений Яндекса

API – чрезвычайно важная составляющая любого продукта.

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

Должен не допускать совершения странных или ненужных действий и защищать от глупых ошибок, оперативно сообщая о нарушении «контракта».

Если у системы есть такой API, она быстро обретает лояльных пользователей и обзаводится удобными «обертками» для разных платформ и языков программирования.

API Amazon Simple Queue Service (API AWS SQS) — пример такого интерфейса, проверенный временем и огромным количеством клиентов.

Поэтому мы решили не придумывать уникальный интерфейс для Yandex Message Queue, а реализовали поддержку AWS SQS API, причем очень аккуратно.

В большинстве случаев пользователю SQS достаточно сменить конечную точку (адрес сервиса), регион (на данный момент мы используем только «ru-central1») и получить новые учетные данные для доступа в Яндекс.

Облако.

Все остальное, например скрипт, использующий Командная строка AWS , напишите код с помощью AWS SDK или готового сервиса на Сельдерей или бото , скорее всего, трогать его не придется.

Логика и функциональность службы очередей останутся прежними.



Архитектура сервиса распределенной очереди сообщений в Яндекс.
</p><p>
Облаке

Подробное описание методов API очереди сообщений Яндекса находится в сервисная документация .



Немного об удобстве

Yandex Message Queue — управляемый сервис, то есть Яндекс.

Облако отвечает за работоспособность серверов и программного обеспечения.

Команда сервиса следит за исправностью очередей, оперативно заменяет вышедшие из строя диски, устраняет обрывы сети и выкатывает обновления.

Обновление происходит без остановки сервиса: пока мы устанавливаем новую версию YMQ на одну группу серверов, балансировщик нагрузки старательно перенаправляет трафик на другие.

Таким образом, пользователи ничего не замечают. Чтобы вам было удобнее контролировать работу очередей, мы добавили в YMQ большое количество наглядных графиков; здесь показана лишь небольшая часть из них.

Графики находятся в консоли Яндекс.

Облака, в разделе «Статистика».



Архитектура сервиса распределенной очереди сообщений в Яндекс.
</p><p>
Облаке

Мы расскажем вам о четырех наиболее полезных, на наш взгляд, графиках:

  • Расписание «Сообщения в очереди» помогает отслеживать накопление данных в очереди.

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

  • Расписание «Возраст самого старого сообщения в очереди» : Большие значения указывают на проблемы с обработкой сообщений.

    Если все работает правильно, сообщения не должны долго сидеть в очереди.

  • Расписание «Количество попыток прочитать сообщение» показывает, когда сообщения начинают читаться несколько раз.

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

  • Расписание «Время очереди» сообщает, сколько времени проходит с момента отправки сообщения в очередь до момента его получения обработчиком.

Графики помогают мгновенно оценить динамику очереди и наличие сбоев без необходимости просмотра логов.

Более-менее общие моменты мы обсудили, теперь перейдем к деталям.



Как Яндекс Очередь сообщений использует базу данных Яндекса

Сервис «Очередь сообщений Яндекса» построен на базе геораспределенной отказоустойчивой базы данных.

База данных Яндекса (YDB) , который обеспечивает строгую согласованность и поддержку транзакций ACID. Мы не будем сейчас анализировать его структуру и характеристики; мы ограничимся общей схемой.



Архитектура сервиса распределенной очереди сообщений в Яндекс.
</p><p>
Облаке

Очередь в YMQ состоит из логических шардов, представленных определенным фиксированным набором таблиц YDB. Каждая таблица хранит свою часть информации.

Например, существует общая таблица состояний под названием State, в которой хранятся смещения и фактическое количество сообщений.

Есть таблица с данными сообщения и метаданными.

Есть таблица со связанными атрибутами.

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

Данные внутри таблиц очередей являются источником абсолютной истины.

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

Мы храним информацию в нескольких репликах: по одной копии в каждом из трёх дата-центров Яндекса.

Если один из дата-центров недоступен, количество реплик в остальных увеличивается вдвое.

Таким образом восстанавливается необходимый уровень надежности.

Даже если выйдет из строя весь дата-центр и одна сервисная стойка в другой, данные будут полностью доступны.



Первая версия архитектуры очереди сообщений Яндекса.

Первая версия архитектуры YMQ, которую мы сами называли наивной, выглядела так.



Архитектура сервиса распределенной очереди сообщений в Яндекс.
</p><p>
Облаке

На диаграмме показан путь HTTPS-запроса от клиента YMQ к хранилищу YDB. Давайте рассмотрим основные компоненты:

  1. Балансировщик L3 отправляет запрос в ближайший к пользователю дата-центр Яндекса.

    Это снижает задержку сети, хотя нагрузка распределяется неравномерно.

  2. Nginx на виртуальной машине Яндекс.

    Облака терминирует HTTPS-соединения, обеспечивает защиту от сетевых атак и проксирует запрос дальше на сервер YMQ, уже в HTTP.

  3. HTTP-сервер YMQ реализует логику HTTP API SQS, выполняет проверку и преобразует запрос в строго типизированный формат protobuf.
  4. Система актеров YMQ – система актеров .

    В нем одновременно работают тысячи различных субъектов, обменивающихся информацией.

    Система актеров каждого хоста является частью кластера.

    Все акторы кластера живут и действуют как единое целое.

    Бизнес-логика YMQ реализована в различных субъектах, которые выполняют запросы транзакций к YDB.

  5. Планшеты YDB («таблетки») — часть ядра YDB, отвечающая за работу с таблицами в запросах и транзакциях.

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

  6. СХД – надежное, распределенное, отказоустойчивое хранилище.

У такой архитектуры есть недостаток: все серверы кластера независимо работают с таблицами одной очереди.

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

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



Архитектура очереди сообщений Яндекса с мастерами очередей

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

Это было очень мало.

Основной проблемой было несовпадение запросов.

Большое количество логически конфликтующих транзакций быстро привело к тому, что скрытые кэши сообщений стали несогласованными.

Для решения проблемы мы ввели специальную сущность — мастер очереди.



Архитектура сервиса распределенной очереди сообщений в Яндекс.
</p><p>
Облаке

Мастер очереди — актор, который в обычных условиях существует в единственном экземпляре в кластере и пропускает через себя все запросы, связанные с конкретной очередью.

Если запрос к очереди поступает на сервер, где требуемый мастер недоступен, специальный прокси-актор перенаправляет запрос, а затем передает обратно ответ, полученный от мастера.

При использовании мастера очереди правильный кэш разблокированных сообщений уменьшает конфликты при работе с таблицами.

Упрощается реализация ограничения потока запросов, например, через Дырявое ведро .

Доступны быстрые и точные метрики очереди: количество сообщений, общий трафик и тому подобное.

Вы можете группировать запросы одного типа.

Теоретически такая архитектура имеет определенные недостатки, связанные с централизацией:

  1. Снижение отказоустойчивости: при выходе из строя виртуальной машины с мастером все очереди с мастерами на ней будут недоступны.

    Однако специальные механизмы YDB позволяют поднимать новые мастера внутри кластера всего за несколько секунд. Это во многом решает проблему.

  2. Ограниченная масштабируемость: все запросы проходят через один хост. Недостаток устраняют таблетки YDB. Именно они выполняют всю тяжелую работу с данными.

    А мастер асинхронно отправляет запросы и обрабатывает полученные результаты.

    Это делает его легковесным объектом, который не создает узких мест во время нагрузочного тестирования.



Пакетирование запросов в мастерах очередей

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

Лучше превратить сто транзакций для записи сообщений по одному в одну транзакцию для записи ста сообщений одновременно.

С мастерами очередей гораздо проще реализовать такую пакетную обработку (пакетную обработку).



Архитектура сервиса распределенной очереди сообщений в Яндекс.
</p><p>
Облаке

Пакетная обработка немного увеличивает задержку при выполнении операций.

В свою очередь, пропускная способность значительно увеличивается.

При пакетной обработке очередь из одного сегмента может обрабатывать до 30 000 запросов в секунду.

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

Нам нужно было оптимизировать работу с очередями с помощью гибкого алгоритма.

Фронтальные варианты с накоплением сообщений в буфере до порогового числа или сбросом по таймеру нас не устроили.

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

Его работа показана в формате временной диаграммы.



Архитектура сервиса распределенной очереди сообщений в Яндекс.
</p><p>
Облаке

Здесь при поступлении нового сообщения возможен один из трех сценариев:

  1. Транзакция начинается мгновенно, если нет других транзакций того же типа.

  2. Если есть выполняемые транзакции, сообщение добавляется в буфер и ожидает завершения транзакций.

  3. Если размер буфера превышает порог, запускается еще одна параллельная транзакция.

    Количество транзакций, выполняемых параллельно, ограничено.

Идея адаптивного пакетирования напоминает Алгоритм Нэгла для TCP/IP. И вот что интересно: по результатам нагрузочных тестов мы обнаружили, что адаптивная пакетная обработка немного снижает задержку операций.

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

По сумме факторов решение с адаптивным пакетированием превзошло первое наивное решение во всех сценариях.



Что происходит с мастерами, когда возникают проблемы?

В Очереди сообщений Яндекса, как и в любой распределенной системе, могут возникать нештатные ситуации.

Серверы выходят из строя, диски тормозят, сеть выходит из строя внутри центров обработки данных и между ними.

В таких случаях YDB автоматически переносит затронутые планшеты на более подходящие серверы в кластере в течение нескольких секунд. Мастера очередей YMQ носят с собой вместе со своими планшетами.

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



Архитектура сервиса распределенной очереди сообщений в Яндекс.
</p><p>
Облаке

Для YMQ это не проблема.

Запросы к базе данных не делают предположений о достоверности кэша видимых сообщений и проверяют каждое из них заново в процессе сокрытия.

Поэтому существование «лишних» мастеров приводит лишь к небольшому временному снижению производительности.



Как мы добились отказоустойчивости при создании очереди

В YDB невозможно создавать несколько таблиц и изменять данные в рамках одной транзакции.

Для нас это означало, что очередь, которая физически представляет собой набор таблиц, не может быть создана «транзакционно».

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

Мы подумали об этом и разработали следующую схему решения проблемы.



Архитектура сервиса распределенной очереди сообщений в Яндекс.
</p><p>
Облаке

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

Таким образом создаются версии, которые в конечном итоге «фиксируются» в виде строки в специальную таблицу.

Выбирается выигрышная версия, и все «проигрышные» запросы понимают, какая версия «выиграла», и возвращают правильную ссылку.

Такой алгоритм в парадигме «все или ничего» устойчив к сбоям за счет независимости создаваемых структур и наличия финальной транзакции с коммитом версии.

Если фиксация прошла успешно, можно считать, что запрошенная очередь создана корректно.



Как организовано тестирование и мониторинг в Очереди сообщений Яндекса

Очередь сообщений Яндекса — это сложный программно-аппаратный комплекс.

У него много возможных точек отказа.

Мы должны быть уверены в качестве предоставляемых нами услуг.

Поэтому мы регулярно тестируем его.

  • Модульные тесты проверить корректность парсинга запросов, валидацию параметров, атрибутов и т. д. Эти тесты максимально быстрые и узконаправленные.

  • Функциональные тесты позволяют проверить полную функциональность реализованного API в разных ситуациях, обычных и не очень.

    Довольно долго, их нужно запускать после каждого изменения кода в репозитории.

  • Системные тесты делать то, что сделал бы настоящий пользователь: создавать и удалять очереди, настраивать необходимые параметры и затем запускать через них ряд сообщений.

    Они проверяют, чтобы ничего не потерялось и не изменилось.

    Также они проверяют граничные значения параметров и корректность обработки некорректного поведения.

    Реализованные с помощью библиотеки boto, они работают 24/7, в том числе на производственном кластере под видом отдельного пользователя.

  • Нагрузочные тесты использовать тот же код, что и системные, но работать с повышенной частотой запросов и количеством сообщений.

    Они неоднократно помогали нам находить узкие места в архитектуре.

Помимо запуска тестов важно следить за состоянием производства.

Для этого у нас есть различные онлайн-проверки и графики с установленными пороговыми значениями и настроенными оповещениями.

Оповещения направляются дежурным.

Вся наша команда участвует в ежедневных обязанностях.

Прежде всего, мы отслеживаем:

  • количество ошибок с кодом 5хх, которых в идеале вообще не должно быть;
  • количество неудачных транзакций YDB;
  • загруженность процессоров, дисков, сети, актерской системы кластера;
  • корректность и производительность загрузки модели, имитирующей действия реального пользователя;
  • работоспособность виртуальных машин Яндекс.

    Облака с нашим сервисом и всем сопутствующим оборудованием.

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



Окончательно

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

Внутри компании наш сервис очередей давно доказал свою полезность и стал частью архитектуры Яндекс.

Видео, Яндекс.

Маркет, Яндекс.

Образование, Яндекс.

Такси и других сервисов.

Теперь он доступен в экосистеме Яндекс.

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

Теперь новые пользователи при регистрации получают денежный грант на обзор, так что пытаться Очередь сообщений Яндекса бесплатна.

Теги: #облачные сервисы #архитектура #Анализ и проектирование систем #Распределенные системы #Промышленное программирование #Команда Яндекс.

облака #Яндекс.

Облако #очереди сообщений #ymq #sqs

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.