Какие существуют уровни гарантии доставки сообщений в брокерах

Ответ

Гарантия доставки — это соглашение о надёжности, которое система обязуется выполнить при передаче сообщения от отправителя (producer) к получателю (consumer). Существует три основных уровня:

  1. At-most-once (не более одного раза)

    • Сообщение доставляется 0 или 1 раз. Это самый быстрый, но наименее надёжный уровень. Сообщения могут быть утеряны при сбое сети или падении потребителя до подтверждения.
    • Применение: Некритические данные, где потеря сообщения допустима (например, сбор метрик, логов).
  2. At-least-once (как минимум один раз)

    • Гарантирует, что сообщение будет доставлено, но допускает дубликаты. Потребитель должен подтвердить (acknowledge) получение сообщения. Если подтверждение не приходит, брокер отправляет сообщение повторно.
    • Применение: Самый распространённый сценарий, где важна доставка, а потребитель спроектирован идемпотентным (повторная обработка дубля не вызывает проблем).
  3. Exactly-once (ровно один раз)

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

Пример реализации At-least-once в RabbitMQ:

import pika

connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()

# 1. Объявляем очередь как 'durable', чтобы она пережила перезапуск брокера.
channel.queue_declare(queue='task_queue', durable=True)

# 2. Включаем подтверждения от брокера (Publisher Confirms).
channel.confirm_delivery()

# 3. Отправляем сообщение с 'delivery_mode=Persistent',
# чтобы оно сохранялось на диск.
channel.basic_publish(
    exchange='',
    routing_key='task_queue',
    body='Task message',
    properties=pika.BasicProperties(
        delivery_mode=pika.DeliveryMode.Persistent
    )
)
print(" [x] Sent 'Task message'")
connection.close()

На стороне потребителя для этой схемы необходимо отправлять подтверждения (channel.basic_ack()) после успешной обработки сообщения.

Ответ 18+ 🔞

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

1. At-most-once (не более одного раза) Это когда ты такой: «Отправил и похуй». Сообщение может долететь, а может и нет, как повезёт. Быстро? Ебать как быстро! Надёжно? Ну, на уровне «авось пронесёт». Если сеть чихнула или потребитель упал раньше, чем успел моргнуть — всё, пиши пропало, сообщение в тартарары.

  • Куда совать: Всякую несущественную хуйню, которую не жалко. Типа метрик или логов. Потерял пару записей — и хуй с ними.

2. At-least-once (как минимум один раз) А вот это уже серьёзнее. Тут система тебе клянётся: «Чувак, сообщение дойдёт, я тебе божусь!». Но есть нюанс, ёпта: оно может дойти несколько раз. Потребитель должен чётко сказать брокеру: «Окей, я всё съел, спасибо» (это называется ack). Если этого «спасибо» не последовало, брокер, такой настойчивый уёбок, пришлёт сообщение снова. И снова. Пока не убедится.

  • Куда совать: Да почти везде, это самый популярный вариант. Главное — сделать своего потребителя идемпотентным, чтобы он, получив дубль, не обосрался, а спокойно его проигнорировал.

3. Exactly-once (ровно один раз) А это, блядь, высший пилотаж и головная боль одновременно. Сообщение должно быть доставлено и обработано ровно один раз, ни больше, ни меньше. Никаких «ой, прости, я уже это видел». Тут уже в ход идут транзакции, дедупликация и прочая магия, от которой мозги сворачиваются в трубочку.

  • Куда совать: Туда, где дубликаты — это пиздец и разорение. Ну, типа банковских переводов. Представь, что платёж ушёл дважды — волосы дыбом встанут, да?

Ну а теперь, как это выглядит в коде для «At-least-once» в RabbitMQ:

Смотри, тут всё по полочкам, чтобы твоё сообщение не сгинуло в небытии.

import pika

connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()

# 1. Объявляем очередь как 'durable', чтобы она пережила перезапуск брокера.
channel.queue_declare(queue='task_queue', durable=True)

# 2. Включаем подтверждения от брокера (Publisher Confirms).
channel.confirm_delivery()

# 3. Отправляем сообщение с 'delivery_mode=Persistent',
# чтобы оно сохранялось на диск.
channel.basic_publish(
    exchange='',
    routing_key='task_queue',
    body='Task message',
    properties=pika.BasicProperties(
        delivery_mode=pika.DeliveryMode.Persistent
    )
)
print(" [x] Sent 'Task message'")
connection.close()

И запомни, ёпта: если ты так шлёшь, то на той стороне потребитель обязан после успешной обработки отправить подтверждение — channel.basic_ack(). Иначе брокер решит, что сообщение не дошло, и начнёт закидывать тебя им снова, как навязчивый продавец.