Mecanismo de Failover - santig005/Distributed-Systems-gRPC GitHub Wiki
Mecanismo de Failover con MOM (RabbitMQ)
Este sistema utiliza RabbitMQ como Middleware Orientado a Mensajes (MOM) para implementar un mecanismo de failover, principalmente para la operación de creación de órdenes.
Propósito
El objetivo es evitar la pérdida de solicitudes de creación de órdenes si uno de los servicios requeridos (User Service, Product Service) o el propio Order Service están temporalmente indisponibles o fallan durante el procesamiento de una manera que se considera recuperable (ej., error de red temporal, error interno momentáneo).
Configuración de RabbitMQ
- Cola Principal: Se utiliza una cola dedicada llamada
order-service-queue
. - Durabilidad: La cola se declara como
durable: true
. Esto significa que los mensajes en la cola sobrevivirán a un reinicio del broker RabbitMQ. - Acknowledgements (ACKs): El consumidor en el
order_service
está configurado connoAck: false
. Esto requiere que el consumidor envíe explícitamente un reconocimiento (channel.ack(msg)
) a RabbitMQ después de procesar exitosamente un mensaje. Si el consumidor falla antes de enviar el ACK, RabbitMQ re-entregará el mensaje (a este u otro consumidor disponible).
Flujo de Failover (Creación de Orden)
- Petición Inicial: El API Gateway recibe un
POST /api/orders
. - Llamada gRPC: El Gateway llama al
createOrder
delOrderServiceClient
. - Procesamiento en Order Service: El
createOrderHandler
enorder_service
intenta:- Obtener datos del usuario (
userClient.GetUser
). - Obtener datos de los productos (
productClient.GetProduct
). - Verificar saldo.
- Actualizar saldo del usuario (
userClient.UpdateUserBalance
).
- Obtener datos del usuario (
- Detección de Error: Si alguna de las llamadas gRPC a
user_service
oproduct_service
falla con un error considerado "retryable" (comoUNAVAILABLE
,INTERNAL
,DEADLINE_EXCEEDED
), o si ocurre un error interno similar antes de crear la orden localmente, elcatch
encreateOrderHandler
(o la lógica en el API Gateway si el error ocurre allí) intercepta el fallo. - Decisión de Encolar: El
GatewayGrpcErrorHandler
(en el API Gateway) o una lógica similar determina si el error (StatusRuntimeException
) es retryable. - Publicación en RabbitMQ: Si el error es retryable, el API Gateway no intenta procesar más, sino que serializa la solicitud original (
userId
,productIds
) como un mensaje JSON y lo envía a la colaorder-service-queue
en RabbitMQ usandoRabbitMQService
. - Respuesta al Cliente: El API Gateway responde inmediatamente al cliente con
202 Accepted
, indicando que la solicitud fue recibida pero será procesada más tarde. - Consumo Asíncrono (Order Service): El consumidor RabbitMQ dentro del
order_service
(startRabbitMQConsumer
enserver.js
) está escuchando constantemente en la colaorder-service-queue
. - Recepción del Mensaje: Cuando un mensaje llega, el consumidor lo recibe. Gracias a
channel.prefetch(1)
, solo procesa un mensaje a la vez. - Intento de Procesamiento: El consumidor deserializa el mensaje JSON y llama internamente a la lógica de
createOrderHandler
con los datos recuperados (userId
,productIds
). - Éxito: Si esta vez
createOrderHandler
se ejecuta sin errores (porque los servicios dependientes ya están disponibles), procesa la orden, la guarda enorders.json
, y finalmente llama achannel.ack(msg)
. RabbitMQ elimina el mensaje de la cola. - Fallo Persistente / Error No Retryable en Consumidor:
- Si el procesamiento dentro del consumidor vuelve a fallar con un error retryable, el consumidor llama a
channel.nack(msg, false, true)
después de un backoff.requeue=true
le dice a RabbitMQ que vuelva a poner el mensaje en la cola para otro intento (potencialmente por el mismo consumidor después del backoff u otro si hubiera más instancias). - Si el procesamiento falla con un error no retryable (ej: datos inválidos en el mensaje, saldo insuficiente persistente), el consumidor llama a
channel.ack(msg)
para eliminar el mensaje de la cola y evitar bucles infinitos. El error se registra en los logs delorder_service
. Este mensaje se consideraría "perdido" en términos de procesamiento automático y requeriría intervención manual o una estrategia de "Dead Letter Queue" (no implementada actualmente).
- Si el procesamiento dentro del consumidor vuelve a fallar con un error retryable, el consumidor llama a