En artículos anteriores se explicaron los conceptos básicos de exchanges y colas en RabbitMQ. Ahora surge una pregunta crítica: al delegar mensajes a un sistema intermedio como RabbitMQ, ¿cómo garantizamos que no se pierdan durante el proceso? La confiabilidad se asegura principalmente desde tres frentes: productor, broker (RabbitMQ) y consumidor.
Confianza en el productor
Se refiere a cómo garantizar que los mensajes enviados por el productor llleguen correctamente al exchange o cola destino.
1. Mecanismo de reintento
Para manejar fallos transitorios como caídas de red o timeouts antes de que el mensaje alcance el exchange:
spring:
rabbitmq:
connection-timeout: 1s
template:
retry:
initial-interval: 1000ms
multiplier: 2
max-attempts: 3
| Parámetro | Descripción |
|---|---|
| connection-timeout | Tiempo máximo para establecer conexión; si excede, se considera falllido. |
| initial-interval | Retraso inicial antes del primer reintento. |
| multiplier | Multiplicador aplicado al intervalo entre reintentos. |
| max-attempts | Número máximo de intentos antes de rendirse. |
⚠️ Nota: Los reintentos bloquean el hilo actual. En entornos de alta concurrencia, ajusta cuidadosamente los parámetros o evita su uso indiscriminado.
2. Callbacks de confirmación y retorno
Verifican si el mensaje llegó al exchange y fue enrutado correctamente.
ConfirmCallback
Confirma la recepción del mensaje por parte del exchange:
spring:
rabbitmq:
publisher-confirm-type: correlated
- none: Sin confirmaciones.
- simple: Espera síncrona de ACK/NACK.
- correlated: Confirmación asíncrona con contexto asociado.
CorrelationData data = new CorrelationData(UUID.randomUUID().toString());
rabbitTemplate.setConfirmCallback((corrData, ack, reason) -> {
if (ack) {
System.out.println("✅ Mensaje confirmado - ID: " + corrData.getId());
} else {
System.out.println("❌ Falló entrega - ID: " + corrData.getId() + ", causa: " + reason);
}
});
ReturnCallback
Se activa cuando un mensaje no pudo ser enrutado a ninguna cola:
spring:
rabbitmq:
publisher-returns: true
rabbitTemplate.setReturnsCallback(returned -> {
System.err.println("📬 Mensaje no enrutado: " + returned.getMessage());
// lógica de recuperación personalizada
});
Confianza en el broker (RabbitMQ)
Se logra mediante persistencia de exchanges, colas y mensajes. Por defecto, Spring AMQP persiste los mensajes.
Para enviar mensajes no persistentes:
Message msg = MessageBuilder.withBody("Hola mundo".getBytes(StandardCharsets.UTF_8))
.setDeliveryMode(MessageDeliveryMode.NON_PERSISTENT)
.build();
La persistencia afecta el rendimiento porque los mensajes se guardan primero en memoria y luego se descargan a disco bajo presión. Para mitigar esto, RabbitMQ introdujo las colas perezosas (lazy queues) desde la versión 3.6:
- Los mensajes se escriben directamente en disco.
- Solo se cargan en memoria (máx. 2048) según la velocidad del consumidor.
A partir de la versión 3.12, todas las colas son lazy por defecto.
Confianza en el consumidor
El consumidor debe confirmar explícitamente el procesamiento de cada mensaje:
- ack: Procesado con éxito → mensaje eliminado.
- nack: Error temporal → reencolado para reintentar.
- reject: Error irrecuperable → meensaje descartado o redirigido.
⚠️ Riesgo: Si un mensaje genera nack repetidamente, puede causar bucles infinitos. Solución: combinar con mecanismos de reintento local y colas de muerte (DLQ).
Ejemplo de estrategia de reintento con Spring Retry + DLQ:
@RabbitListener(queues = "mi-cola")
public void procesarMensaje(String contenido) {
try {
// lógica de negocio
} catch (Exception e) {
// tras N fallos locales, publicar a DLQ automáticamente
throw e;
}
}