Ответ
Чтобы уведомления не терялись и не дублировались, я строю pipeline на основе идемпотентности, подтверждений и dead letter queues.
1. Архитектура с гарантированной доставкой:
- В качестве брокера сообщений для алертов использую RabbitMQ (с поддержкой подтверждений) или Apache Kafka (с семантикой "at least once" и оффсетами).
- Alertmanager от Prometheus может отправлять алерты в вебхук, который я пишу сам. Этот вебхук не просто ретранслирует, а помещает алерт в очередь.
2. Обработка с подтверждением (Acknowledgement):
- Потребитель (worker, отправляющий в Slack/Telegram/Email) берёт сообщение из очереди, обрабатывает и отправляет явное подтверждение (ACK) только после успешной доставки во внешнюю систему. Пока ACK не отправлен, сообщение остаётся в очереди и может быть обработано другим воркером в случае сбоя.
- Пример логики на Python с Pika (RabbitMQ):
import pika
def callback(ch, method, properties, body): alert_data = json.loads(body) try:
Попытка отправить уведомление в Slack
send_to_slack(alert_data)
# Если успешно — подтверждаем обработку
ch.basic_ack(delivery_tag=method.delivery_tag)
print(f"Ack sent for alert: {alert_data['alertname']}")
except Exception as e:
print(f"Failed to send alert: {e}")
# При неудаче — отрицательное подтверждение с requeue=True
ch.basic_nack(delivery_tag=method.delivery_tag, requeue=True)
connection = pika.BlockingConnection(pika.ConnectionParameters('rabbitmq')) channel = connection.channel() channel.queue_declare(queue='alerts', durable=True) channel.basic_consume(queue='alerts', on_message_callback=callback, auto_ack=False) channel.start_consuming()
**3. Идемпотентность обработчика:**
* Мой обработчик уведомлений идемпотентен. Если один и тот же алерт (идентифицируемый по labels и timestamp) пришёл дважды, второй вызов не создаст дублирующего сообщения для пользователя. Для этого можно использовать простой in-memory кэш (с TTL) или запись ID обработанных алертов в Redis.
**4. Dead Letter Queue (DLQ) для сбойных сообщений:**
* Настраиваю в RabbitMQ политику DLQ. Если сообщение было отклонено (NACK) или не обработано после N попыток ретрая (например, 5), оно автоматически перемещается в отдельную очередь `alerts.dlq`.
* Сообщения в DLQ я мониторю отдельным алертом. Это позволяет вручную разобраться с проблемными алертами, которые не могут быть доставлены (например, из-за неверного формата или недоступности конечной системы).
**5. Мониторинг самого pipeline:**
* Сбор метрик: размер очереди алертов, количество сообщений в DLQ, latency обработки.
* Алерт на застревание очереди (если её размер растёт и не уменьшается), что сигнализирует о проблеме в воркерах уведомлений.