Pull to refresh

Проектирование и нейминг очередей

Reading time4 min
Views3.3K
Несколько правил как организовать точки, очереди и как их правильно именовать, чтобы было удобно.

| exchange   | type  | binding_queue     | binding_key  |
|-------------------------------------------------------|
| user.write | topic | user.created.app2 | user.created |

Понятия
AMQP (Advanced Message Queuing Protocol) — открытый протокол для передачи сообщений между компонентами системы.
Поставщик (Publishers/Producer) — программа, отправляющая сообщения.
Подписчик (Consumer) — программа, принимающая сообщения. Обычно подписчик находится в состоянии ожидания сообщений.
Очередь (Queue) — очередь сообщений.
Точка обмена (Exchange) — изначальная точка обмена очереди, которая занимается маршрутизацией.

Правило 1


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

Правило 2


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

Правило 3


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

Примеры и решения


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

| object | event   |
|------------------|
| user   | created |
| user   | updated |
| user   | deleted |

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

Прежде чем ответить на этот вопрос, я хочу задать другой вопрос: действительно ли нам нужна отдельная точка для этого случая? Что если мы абстрагируем все 3 типа событий как событие «write», подтипы которого «created», «updated» и «deleted»?

| object | event   | sub-type |
|-----------------------------|
| user   | write   | created  |
| user   | write   | updated  |
| user   | write   | deleted  |

Решение 1


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

Решение 2


Самое простое решение не может работать, когда есть второе приложение (имеющее другую логику обработки), которое хочет подписаться на любые сообщения, опубликованные в очереди. Когда подписаны несколько приложений, нам, по крайней мере, нужна одна точка с типом «fanout» с привязками к нескольким очередям. Таким образом, сообщения отправляются в точку, и она дублирует сообщения в каждую очередь. Каждая очередь представляет задание обработки для каждого приложения.

| queue           | subscriber  |
|-------------------------------|
| user.write.app1 | app1        |
| user.write.app2 | app2        |

| exchange   | type   | binding_queue   |
|---------------------------------------|
| user.write | fanout | user.write.app1 |
| user.write | fanout | user.write.app2 |

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

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

Решение 3


Чтобы решить проблему выше, мы должны извлечь событие «user.created» из типа «user.write». Точка обмена с типом «topic» может нам помочь. При публикации сообщений будем использовать user.created/user.updated/user.deleted как ключи роутинга в точке, так мы сможем поставить ключ связи «user.*» в очереди «user.write.app1» и ключ связи «user.created» в очереди «user.created.app2».

| queue             | subscriber  |
|---------------------------------|
| user.write.app1   | app1        |
| user.created.app2 | app2        |

| exchange   | type  | binding_queue     | binding_key  |
|-------------------------------------------------------|
| user.write | topic | user.write.app1   | user.*       |
| user.write | topic | user.created.app2 | user.created |

Решение 4


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

| queue             | subscriber  |
|---------------------------------|
| user.write.app1   | app1        |
| user.created.app2 | app2        |

| exchange   | type   | binding_queue    | binding_key   |
|--------------------------------------------------------|
| user.write | direct | user.write.app1   | user.created |
| user.write | direct | user.write.app1   | user.updated |
| user.write | direct | user.write.app1   | user.deleted |
| user.write | direct | user.created.app2 | user.created |

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

Решение 5


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

| queue              | subscriber  |
|----------------------------------|
| user.write.app1    | app1        |
| user.created.app2  | app2        |
| user.behavior.app3 | app3        |

| exchange      | type  | binding_queue      | binding_key     |
|--------------------------------------------------------------|
| user.write    | topic | user.write.app1    | user.*          |
| user.write    | topic | user.created.app2  | user.created    |
| user.behavior | topic | user.behavior.app3 | user.*          |

Вольный перевод статьи RabbitMQ Exchange and Queue Design Trade-off.
Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
+5
Comments0

Articles

Change theme settings