Skip to main content

The Pub-sub Abstraction

Что такое система pub-sub?​

Publish-subscribe (pub-sub) система — это асинхронный метод взаимодействия между сервисами, который популярен в бессерверных и микросервисных архитектурах. В системе pub-sub сообщения могут отправляться асинхронно различным подсистемам системы. Все сервисы, подписанные на модель pub-sub, получают сообщение, которое отправлено в систему.

Пример​

Например, когда известный спортсмен публикует пост в Instagram или делится твитом, все его подписчики получают обновление. В этом случае спортсмен является издателем (publisher), его пост или твит — это сообщение (message), а все его подписчики — подписчики (subscribers).

img_41.png

Мотивация​

Аппаратная инфраструктура распределенных систем состоит из миллионов машин. Использование pub-sub системы для асинхронной передачи сообщений увеличивает масштабируемость. Производители (producers) и потребители (consumers) рассоединены и работают независимо, что позволяет масштабировать и развивать их отдельно. Декуплинг (разделение) компонентов — производителей и потребителей — обеспечивает большую масштабируемость, поскольку добавление или удаление любого компонента не влияет на другие компоненты.

Как спроектировать систему pub-sub?​

Мы разделили проектирование системы pub-sub на следующие уроки:

  1. Введение: В этом уроке мы рассмотрим случаи использования системы pub-sub, определим ее требования и спроектируем API.
  2. Дизайн: В этом уроке мы рассмотрим два варианта дизайна системы pub-sub: один с использованием очередей сообщений, а другой — с брокером.

Introduction to Pub-sub​

Система pub-sub предлагает асинхронное взаимодействие, и вот несколько примеров, где она может быть полезна:

Улучшенная производительность​

Система pub-sub обеспечивает push-распространение информации, что устраняет необходимость в регулярных проверках изменений со стороны получателей сообщений. Это способствует более быстрому времени отклика и снижает задержку доставки сообщений.

Обработка логов и данных​

Pub-sub помогает в обработке большого количества логов и данных о взаимодействии пользователей. Эти данные могут быть использованы для анализа поведения пользователей. Мы можем передавать большое количество данных в систему pub-sub, которая затем может доставить их в любую аналитическую систему для понимания пользовательских паттернов поведения. Например, крупные сервисы, такие как Meta, используют систему pub-sub под названием Scribe для определения того, кто нуждается в данных, и для удаления или архивации обработанных или ненужных данных. Это необходимо для управления огромными объемами данных.

Мониторинг в реальном времени​

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

Репликация данных​

Система pub-sub может использоваться для распределения изменений. Например, в протоколе лидер-последователь лидер отправляет изменения своим последователям через систему pub-sub. Это позволяет последователям асинхронно обновлять свои данные. Распределенные кэши также могут обновляться, получая модификации асинхронно. Приложения, такие как WhatsApp, которые позволяют просматривать один и тот же разговор на нескольких устройствах, могут эффективно работать с использованием pub-sub, где разные устройства могут выступать как издателем, так и подписчиком.

img_42.png


What are the similarities and differences between a pub-sub system and queues?

Details

The pub-sub system and queues are similar because they deliver information that’s produced by the producer to the consumer. The difference is that only one consumer consumes a message in the queue, while there can be multiple consumers of the same message in a pub-sub system.


How are producers and consumers decoupled from one another in a pub-sub system?

Details

Producers don’t know who’ll end up reading their information. They just send it to the system, and it is read by the consumer. The system acts as a decoupling layer between the producers and consumers. Producers are not affected by slow consumers, the count of consumers, or the failure of consumers. We can scale them independently.


Требования​

Наша цель — спроектировать систему pub-sub, которая будет соответствовать следующим требованиям.

Функциональные требования​

Рассмотрим функциональные требования к системе pub-sub:

  1. Создание темы (topic):

    • Производитель должен иметь возможность создавать тему.
  2. Запись сообщений:

    • Производители должны иметь возможность записывать сообщения в тему.
  3. Подписка:

    • Потребители должны иметь возможность подписываться на тему для получения сообщений.
  4. Чтение сообщений:

    • Потребители должны иметь возможность читать сообщения из темы.
  5. Указание времени хранения:

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

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

You’re designing a real-time chat application that has channels for different discussions. Explain how you would use a pub-sub system for its messaging component.
Describe the mapping of the chatting application onto the following pub-sub components:

  • Topics
  • Subscribers
  • How message delivery would work
Details
  • Topics: These would be the channels in the chat application. Each channel represents a topic in the pub-sub system.
  • Subscribers: Users who are part of a channel are considered subscribers. They subscribe to the channel (topic) to receive messages.
  • Message delivery: When a user (publisher) sends a message in a channel, they are publishing to that topic. The pub-sub system then broadcasts this message to all subscribers of the topic, ensuring real-time delivery of messages to all users within the channel.

This setup allows for efficient and scalable messaging within the chat application, as the pub-sub model is well-suited for handling real-time data distribution and event-driven communication.


Нефункциональные требования​

При проектировании системы pub-sub мы учитываем следующие нефункциональные требования:

  1. Масштабируемость:

    • Система должна масштабироваться с увеличением числа тем, а также с увеличением нагрузки на запись (со стороны производителей) и чтение (со стороны потребителей).
  2. Доступность:

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

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

    • Система должна продолжать работать в случае сбоев.
  5. Конкурентность:

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

Дизайн API​

Ниже представлен дизайн API для системы pub-sub. Мы исключим некоторые параметры, такие как идентификатор производителя или потребителя, предполагая, что эта информация доступна из контекста соединения.

Создание темы​

Вызов API для создания темы должен выглядеть следующим образом:

create(topic_ID, topic_name);

img_43.png

Write a message​

The API call to write into the pub-sub system should look like this

write(topic_ID, message);

img_44.png

Read a message​

The API call to read data from the system should look like this:

read(topic_ID);

The topic is found using topic_ID, and the call will return an object containing the message to the caller. img_45.png

Subscribe to a topic​

The API call to subscribe to a topic from the system should look like this

subscribe(topic_ID);

The function adds the consumer as a subscriber to the topic that has the topic_ID. img_46.png

Unsubscribe from a topic​

The API call to unsubscribe from a topic from the system should look like this

unsubscribe(topic_ID);

The function removes the consumer as a subscriber from the topic that has the topic_ID. img_47.png

Delete a topic​

The API call to delete a topic from the system should look like this

delete_topic(topic_ID);

The function deletes the topic on the basis of the topic_ID. img_48.png

Building blocks we will use​

The design of pub-sub utilizes many building blocks that have been discussed in the initial chapters. We’ll consider the following lessons on building blocks.
img_50.png

Database: We’ll use databases to store information like subscription details.
Distributed messaging queue: We’ll use use a messaging queue to store messages sent by the producer.
Key-value: We’ll use a key-value store to hold information about consumers.

Design of a Pub-sub System​

Первый дизайн​

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

Необходимые компоненты​

Перечислим необходимые компоненты:

  1. Очередь темы:

    • Каждая тема будет представлять собой распределенную очередь сообщений, в которую мы будем сохранять сообщения, отправленные производителем. Производитель будет записывать свои сообщения в эту очередь.
  2. База данных:

    • Мы будем использовать реляционную базу данных для хранения данных о подписках. Например, нам нужно хранить информацию о том, какой потребитель подписан на какую тему, чтобы мы могли предоставлять потребителям их запрашиваемые сообщения. Мы выберем реляционную базу данных, поскольку наши данные о потребителях структурированы, и мы хотим обеспечить целостность данных.
  3. Директор сообщений (Message Director):

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

    • Сообщение из очереди темы будет копироваться в очередь потребителя, чтобы он мог его прочитать. Для каждого потребителя мы определим отдельную распределенную очередь.
  5. Подписчик (Subscriber):

    • Когда потребитель запрашивает подписку на тему, этот сервис добавляет запись в базу данных.

Процесс работы системы​

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

Примечание​

Для защиты от сбоев мы будем использовать резервные сервисы (fail-over) для директора сообщений и подписчика. img_51.png

Второй дизайн: Альтернативный подход к проектированию системы pub-sub​

Высокоуровневый дизайн​

Для решения проблемы с большим количеством очередей и дублированием сообщений рассмотрим другой подход к проектированию системы pub-sub. На высоком уровне система pub-sub будет включать следующие компоненты:

  1. Брокер (Broker):

    • Это сервер, который будет обрабатывать сообщения. Он будет хранить сообщения, отправленные производителем, и предоставлять их для чтения потребителям.
  2. Менеджер кластера (Cluster Manager):

    • У нас будет несколько серверов брокеров для удовлетворения потребностей в масштабируемости. Нам нужен менеджер кластера для мониторинга состояния брокеров. Он уведомит нас, если один из брокеров выйдет из строя.
  3. Хранилище (Storage):

    • Мы будем использовать реляционную базу данных для хранения данных о потребителях, таких как информация о подписке и срок хранения сообщений.
  4. Менеджер потребителей (Consumer Manager):

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

Дополнительные аспекты дизайна​

  1. Подтверждение (Acknowledgment):

    • Подтверждение используется для уведомления производителя о том, что полученное сообщение было успешно сохранено. Система будет ждать подтверждения от потребителя, если сообщение было успешно потреблено.
  2. Время хранения (Retention Time):

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

Преимущества второго дизайна​

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

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

img_52.png Let’s understand the role of each component in detail.

Брокер (Broker)​

Брокер — это основной компонент нашей системы pub-sub, который отвечает за обработку запросов на запись и чтение. Каждый брокер будет управлять несколькими темами, и каждая тема может иметь несколько связанных с ней разделов (partitions). Мы используем разделы для хранения сообщений в локальном хранилище с целью обеспечения их долговременной сохранности и улучшения доступности.
img_53.png

Разделы и сегменты​

  • Разделы (Partitions): Разделы содержат сообщения, инкапсулированные в сегментах. Разделение темы на разделы позволяет распределять нагрузку на I/O операции, повышая производительность и масштабируемость системы.
  • Сегменты (Segments): Сегменты помогают определить начало и конец сообщения, используя смещенные адреса (offset addresses). Это позволяет потребителям извлекать сообщения по своему выбору из раздела, читая данные с определенного смещенного адреса.

Преимущества использования разделов​

  1. Устойчивость данных:

    • Тема представляет собой последовательность сообщений, хранящихся в локальном хранилище брокера. Как только данные добавляются в тему, их невозможно изменить, что обеспечивает неизменность данных.
  2. Масштабируемость операций:

    • Чтение и запись сообщений в тему и из нее являются задачами, связанными с I/O операциями, и масштабирование таких задач может быть сложным. По этой причине мы делим темы на несколько разделов, что позволяет распределять данные, относящиеся к одной теме, по разным разделам.

Пример​

Предположим, у нас есть Тема A, для которой мы выделяем три раздела. Производители отправляют свои сообщения в соответствующую тему. Полученные сообщения будут распределяться по различным разделам на основе алгоритма кругового распределения (round-robin). Мы используем вариант этого алгоритма — взвешенное круговое распределение (weighted round-robin), который позволяет учитывать нагрузку на каждый раздел при распределении сообщений.

Преимущества подхода​

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

Таким образом, использование брокеров, разделов и сегментов позволяет эффективно масштабировать систему pub-sub, обеспечивая при этом высокую доступность и надежность данных. img_54.png img_55.png img_56.png img_57.png img_58.png img_59.png img_60.png img_61.png img_62.png


Strict ordering ensures that the messages are stored in the order in which they are produced. How can we ensure strict ordering for our messages?

Details

img_63.png


What problems can arise if all partitions are on the same broker?

Details

If the broker fails or dies, all the messages in the partitions will be lost. To avoid this, we need to make sure that the partitions are spread on different brokers.


Why can’t we use blob stores like S3 to keep messages, instead of the broker’s local storage?

Details

Blob stores like S3 are not optimized for writing and reading short-sized data. If our data is geo-replicated, the above problem is exacerbated.

Therefore, we used the server’s local persistent store with append-based writing. Traditional hard disks are specially tuned to provide good write performance with writing to contiguous tracks or sectors. Reading throughput and latency is also good for contiguous regions of the disk because it allows extensive data caching.


If we use a round-robin algorithm to send messages to a partition, how does the system know where to look when it is time to read?

Details

Our system will need to keep appropriate metadata persistently. This metadata will keep mappings between the logical index of segment or messages to the server identity or partition identifier.

We’ll discuss consumer manager later in the lesson, which will keep the required information.


Распределение разделов между брокерами​

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

Строгое упорядочивание сообщений​

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

Пример распределения разделов​

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

  • Брокеры: В системе есть несколько брокеров, каждый из которых управляет несколькими темами.
  • Темы: Каждая тема разделена на несколько разделов.
  • Распределение разделов: Разделы одной и той же темы распределяются по разным брокерам.

Преимущества распределения разделов​

  1. Масштабируемость: Распределение разделов по разным брокерам позволяет эффективно масштабировать систему, так как нагрузка на обработку сообщений распределяется между несколькими узлами.

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

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

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

img_64.png img_65.png img_66.png

Сегменты и смещения​

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

img_67.png img_68.png

Кластерный менеджер​

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

  1. Реестр брокеров и тем:

    • Сохраняет список тем для каждого брокера.
  2. Управление репликацией:

    • Кластерный менеджер управляет репликацией, используя подход "лидер-последователь". Один из брокеров является лидером. Если он выходит из строя, менеджер определяет, кто станет следующим лидером. В случае сбоя последователя, менеджер добавляет нового брокера и следит за тем, чтобы он стал обновленным последователем. Метаданные обновляются соответственно. Мы будем хранить три реплики каждого раздела на разных брокерах.

img_69.png

Менеджер потребителей​

Менеджер потребителей будет управлять потребителями. У него есть следующие обязанности:

  1. Проверка потребителя:

    • Менеджер извлекает данные из базы данных и проверяет, имеет ли потребитель право на чтение определенного сообщения. Например, если потребитель подписан на Тему A (но не на Тему B), ему не должно быть разрешено читать из Темы B. Менеджер потребителей проверяет запрос потребителя.
  2. Управление временем хранения:

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

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

    • Менеджер потребителей хранит информацию о смещении (offset) для каждого потребителя. Мы будем использовать key-value хранилище для сохранения информации о смещении для каждого потребителя, что обеспечивает быстрое извлечение данных и повышает доступность потребителей. Если Потребитель 1 прочитал от смещения 0 и отправил подтверждение, мы сохраним эту информацию. Таким образом, когда потребитель захочет снова прочитать данные, мы сможем предоставить следующее смещение для чтения сообщения.

Окончательный дизайн​

Ниже представлен окончательный дизайн нашей системы pub-sub. img_70.png

Заключение​

Мы рассмотрели два подхода к проектированию системы pub-sub: с использованием очередей и с использованием нашего кастомного хранилища, оптимизированного для записи и чтения небольших данных.

Существует множество сценариев использования системы pub-sub. Благодаря разделению производителей и потребителей, система может динамически масштабироваться, а сбои хорошо изолированы. Кроме того, благодаря правильному учету потребления данных, pub-sub является предпочтительной системой для крупномасштабных систем, которые генерируют огромные объемы данных. Мы можем точно определить, какие данные нужны, а какие нет.