Direct vs. Topic vs. Fanout: выбор правильного типа обмена
Выбирайте обмены RabbitMQ direct, topic или fanout в зависимости от точной маршрутизации, маршрутизации с подстановочными знаками или широковещательной доставки.
Direct vs. Topic vs. Fanout: выбор правильного типа обмена
Тип обмена RabbitMQ определяет, куда направляются ваши сообщения после публикации продюсером. Неправильный выбор может привести к широковещательной рассылке приватных задач, потере сообщений с несовпадающими ключами маршрутизации или жесткому кодированию правил маршрутизации, что впоследствии создаст проблемы.
Практический выбор прост: используйте direct для точной маршрутизации, fanout для широковещательной рассылки и topic для маршрутизации с подстановочными знаками по схеме именования.
Как работает маршрутизация RabbitMQ
Продюсер публикует сообщение в обмен, а не напрямую в очередь. Обмен сравнивает ключ маршрутизации сообщения с привязками очередей, а затем направляет сообщение в соответствии с типом обмена.
Три важных термина:
- Обмен: принимает опубликованные сообщения.
- Очередь: хранит сообщения до тех пор, пока потребители не получат их.
- Привязка: соединяет очередь с обменом, иногда с ключом привязки.
Если ни одна очередь не соответствует и сообщение не помечено как mandatory, RabbitMQ может отбросить нерутизируемое сообщение. Для важных потоков используйте подтверждения издателя и обрабатывайте возвращенные сообщения.
Прямой обмен (Direct Exchange)
Прямой обмен направляет сообщение в очереди, ключ привязки которых точно совпадает с ключом маршрутизации сообщения.
Используйте его, когда варианты маршрутизации известны и точны. Хорошие примеры включают уровень логирования, тип задачи или очереди, специфичные для арендатора, где достаточно точных имен.
Например, привяжите error_queue с error и info_queue с info. Сообщение, опубликованное с ключом маршрутизации error, попадает в error_queue; сообщение с info — в info_queue. Если две очереди привязаны с error, обе очереди получают копию. Потребители в одной очереди по-прежнему конкурируют за сообщения из этой очереди.
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()
channel.exchange_declare(exchange='logs_direct', exchange_type='direct')
channel.basic_publish(
exchange='logs_direct',
routing_key='error',
body=b'Database connection failed'
)
connection.close()
Выбирайте прямой обмен, когда ваши ключи маршрутизации стабильны и вам не нужно сопоставление с подстановочными знаками.
Широковещательный обмен (Fanout Exchange)
Широковещательный обмен направляет каждое сообщение во все очереди, привязанные к обмену. Он игнорирует ключ маршрутизации.
Используйте его для широковещательных событий, где каждый подписчик должен получить одно и то же сообщение. Типичные случаи включают инвалидацию кэша, события обновления профиля пользователя, уведомления о развертывании или обновления статуса в реальном времени.
Например, широковещательный обмен user_updates может питать три очереди: email_queue, search_index_queue и analytics_queue. Одно опубликованное обновление профиля достигает всех трех сервисов.
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()
channel.exchange_declare(exchange='user_updates', exchange_type='fanout')
channel.basic_publish(
exchange='user_updates',
routing_key='',
body=b'User 123 changed their display name'
)
connection.close()
Выбирайте широковещательный обмен, когда логика маршрутизации не имеет значения и каждая привязанная очередь должна получить копию.
Тематический обмен (Topic Exchange)
Тематический обмен маршрутизирует путем сопоставления ключей маршрутизации, разделенных точками, с ключами привязки с подстановочными знаками. Он обеспечивает гибкую маршрутизацию без создания отдельного обмена для каждого типа событий.
Привязки тематического обмена используют два подстановочных знака:
*соответствует ровно одному слову.#соответствует нулю или более слов.
Например, с ключами маршрутизации, такими как order.created.us и order.cancelled.eu:
order.*.usсоответствует одному действию для заказов в США.order.created.#соответствует созданным заказам во всех регионах и необязательным дополнительным словам.#.euсоответствует событиям, заканчивающимся наeu.
import pika
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()
channel.exchange_declare(exchange='events_topic', exchange_type='topic')
channel.basic_publish(
exchange='events_topic',
routing_key='order.created.us',
body=b'Order 9001 created'
)
connection.close()
Выбирайте тематический обмен, когда потребителям нужны отфильтрованные подмножества более крупного потока событий, например service.environment.severity, entity.action.region или tenant.event.priority.
Краткое руководство по выбору
| Потребность | Тип обмена | Пример |
|---|---|---|
| Точная маршрутизация | Direct | error, invoice.created |
| Широковещательная рассылка всем подписчикам | Fanout | событие очистки кэша |
| Маршрутизация с подстановочными знаками | Topic | orders.*.us, logs.# |
Избегайте использования тематического обмена как свалки для несогласованных ключей маршрутизации. Выберите соглашение об именах заранее, задокументируйте его и упорядочивайте слова от общего к частному или от сущности к действию, в зависимости от того, как ваши потребители фильтруют.
Вывод
Используйте прямые обмены для точных назначений, широковещательные обмены для широковещательной рассылки и тематические обмены, когда потребителям нужны фильтры с подстановочными знаками. Если вы можете описать маршрут как одну точную метку, используйте direct. Если сообщение нужно всем, используйте fanout. Если подписчикам нужны шаблоны, используйте topic.