Amazon Simple Queue Service (SQS) es un poderoso servicio diseñado para administrar las necesidades de mensajería de microservicios robustos y desacoplados.
Se integra fuertemente con AWS Lambda, su socio nativo, como destino objetivo.
SQS puede activar una función Lambda como su objetivo y enviar los elementos del lote como entrada.
Sin embargo, en el lado de la función Lambda, manejar esta entrada puede resultar complicado, especialmente cuando se piensan errores y reintentos.
Este es el tercer artículo de una serie de tres partes sobre las mejores prácticas de SQS
En este artículo, aprenderá sobre las mejores prácticas para las colas de mensajes no entregados y cómo manejar fallas de manera correcta y automatizada mediante la autorreparación redirigiendo los elementos a la cola de origen.
Puede encontrar todos los ejemplos de código Python de AWS CDK aquí .
Puede encontrar todos los ejemplos de código Python de Lambda aquí .
Otros artículos de la serie:
El primer artículo le enseñará cómo manejar de manera eficiente lotes de Amazon SQS con AWS Lambda Powertools para Python y ejemplos de código de AWS CDK.
El segundo artículo le enseñará a manejar fallas de procesamiento por lotes de Amazon SQS y dominar los reintentos automáticos con ejemplos de código de AWS Lambda Powertools para Python y AWS CDK.
Tabla de contenido
Re:cap de SQS
En el primer artículo, en mis prácticas recomendadas de SQS, definimos el patrón de función de SQS a Lambda. Implementamos el código de infraestructura de AWS CDK y el código del controlador de función Lambda.
En el segundo artículo, agregamos la noción de reintentos a la función Lambda y utilizamos la capacidad de marcar fallas parciales en el SQS.
Sin embargo, no manejamos casos de uso en los que, en última instancia, todos los intentos de reintento fallaron.
Tenga en cuenta que este artículo se basa en los ejemplos de código de los artículos anteriores.
Mejores prácticas para las colas de mensajes no entregados de SQS
Repasemos las mejores prácticas de procesamiento por lotes y reintentos de SQS que definimos en los artículos anteriores en el siguiente diagrama:
(1) SQS activa una función Lambda con elementos del lote. La función Lambda itera sobre los elementos del lote y maneja cada elemento y vuelve a intentar su procesamiento (1a en el diagrama, si es necesario) con ' tenacidad '.
(2) La función marca al SQS qué elementos no se procesaron correctamente (después de varios intentos de reintento) o finaliza con una excepción si todo el lote falló. Si todos los elementos se procesan correctamente, la función marca que no se procesaron elementos.
(3) Si Lambda marcó elementos como que no se pudieron procesar, SQS invocará la función nuevamente con un lote que contiene solo los elementos que fallaron una vez que haya transcurrido el tiempo de espera de visibilidad de SQS.
Manejo de fallas
A veces, intentas y vuelves a intentarlo, pero simplemente no funciona. A veces, un servicio externo del que dependes no funciona o puede que tengas un error en tu propio código. Sin importar la causa, tu Lambda no pudo completar el procesamiento de un elemento y, finalmente, el procesamiento falla después de una cantidad predefinida de reintentos.
¿Qué hacemos?
Queremos implementar otro concepto: una cola de cartas muertas (DLQ).
Enviaremos todos los elementos de procesamiento que hayan fallado a otro SQS llamado cola de mensajes fallidos. Revisemos el diagrama actualizado con un nuevo paso, el paso número cuatro.
(4) SQS enviará automáticamente los elementos eventualmente fallidos a otro SQS, el DLQ.
Ahora que todos los elementos fallidos se colocan en una cola y no interfieren con los elementos entrantes del lote regular de SQS, podemos avanzar a la siguiente etapa: el mecanismo de autorreparación y redirección de DLQ.
Rediseño de la cola de cartas muertas
El DLQ contiene elementos del lote que fallaron; ¡genial! Ahora debemos decidir qué hacer con ellos.
Si recuerda, nuestra función Lambda contiene la lógica empresarial para manejar elementos por lotes.
Lo ideal sería mantener la lógica de negocios en esa Lambda en lugar de copiarla a otra Lambda que el DLQ activará con los elementos del lote fallidos.
Afortunadamente, el 6 de junio de 2023, AWS lanzó una nueva API de redireccionamiento que resuelve este dilema.
mover programáticamente mensajes desde la DLQ a su cola original, o a un destino de cola personalizado, para intentar procesarlos nuevamente. - Documentación de AWS
El diseño que propongo es crear un mecanismo de autorreparación. Usaremos una tarea del programador EventBridge que activa una función Lambda que se ejecuta una vez al día (o tantas veces como considere necesario). La función Lambda toma los elementos del lote fallidos del DLQ y los redirige, es decir, los envía de vuelta al SQS original, donde se enviarán a la función Lambda de procesamiento por lotes y se volverán a intentar desde el principio.
Puede utilizar este diseño en cualquier servicio que utilice el patrón SQS --> Lambda.
Si desea obtener más información sobre las tareas del programador EventBridge, lea mi publicación de blog aquí .
Aquí está el diagrama de diseño de redrive:
Dado que la redirección ocurre ocasionalmente, es seguro asumir que el procesamiento por lotes podría tener éxito en intentos posteriores.
Sin embargo, en caso de que se produzcan errores continuos, debe poder supervisarlos y solucionarlos o agregar código en la función SQS Lambda que los ignoró para que no se vuelvan a marcar como fallidos. Puede obtener más información sobre la observabilidad en mi publicación aquí .
Ahora que entendemos el diseño, escribamos el CDK y redirijamos el código de la función Lambda.
Código CDK
Escribamos el código de infraestructura que define nuestro SQS, el destino Lambda y la cola de mensajes fallidos. SQS enviará automáticamente los elementos del lote reintentados que hayan fallado a la cola de mensajes fallidos una vez que los intentos de reintento hayan finalizado en vano.
Ampliaremos el código CDK del primer artículo y agregaremos el DLQ y la tarea programada para la capacidad de redireccionamiento.
En las líneas 12 a 18, definimos el DLQ. Habilitamos el cifrado administrado por SQS para que tengamos cifrado en reposo (¡la seguridad es lo primero!).
En las líneas 24 a 27, conectamos el DLQ al SQS y establecemos el número máximo de recepciones.
El recuento máximo de recepciones se define como:
El número máximo de veces que los consumidores pueden recibir un mensaje. Cuando se supera este valor para un mensaje, este se envía automáticamente a la cola de mensajes no entregados.
Puedes leer más sobre ello aquí .
En las líneas 35 y 36, configuramos una nueva variable de entorno para la función Lambda de redrive. Necesita los ARN de DLQ y SQS para llamar a la API de redrive.
En las líneas 41 a 74, definimos una tarea del programador EventBridge que activa una función Lambda todos los días a las 11:00 p. m., hora de Israel, entre el domingo y el jueves. El código de la función se muestra a continuación. Lambda llamará al SDK de AWS (boto3) para recuperar mensajes del DLQ al SQS de origen. Le otorgamos a la tarea los permisos necesarios para activar la función.
Puede encontrar todos los ejemplos de código CDK aquí .
Función Lambda de Redrive
Puede encontrar el código CDK que define la función de redireccionamiento aquí, en la función '_create_dlq_lambda'.
Tenga en cuenta que debe definir los permisos del rol de la manera menos privilegiada, como se sugiere en la guía para desarrolladores de SQS y no como se presenta en el ejemplo.
Ahora, echemos un vistazo al código del controlador de funciones:
En la línea 12, usamos la biblioteca aws_lambda_env_modeler (que mantengo) para analizar las variables de entorno de la función y obtener los ARN de SQS y DLQ. Si quieres saber por qué es vital validar las variables de entorno, consulta mi publicación .
En las líneas 19 a 27, llamamos a la API de la unidad API 'start_message_move_task' y registramos cualquier excepción.
Eso es todo. Bastante sencillo. Una vez que finaliza la tarea de redirección, los elementos fallidos aparecerán en el SQS y se enviarán a la función Lambda de procesamiento por lotes del SQS para que se procesen nuevamente y, con suerte, esta vez tengan éxito.
Reflexiones finales
El procesamiento por lotes con SQS y Lambda es uno de los casos de uso más básicos de cualquier aplicación sin servidor. Sin embargo, es fácil equivocarse.
A lo largo de tres artículos, tenemos:
Se definieron las mejores prácticas para procesar los artículos por lotes de manera sencilla.
Se implementó el reintento a nivel de Lambda por elemento con 'tenacidad'.
Se implementó el reintento en el nivel SQS
Se implementó un DLQ
Se implementó una capacidad de redireccionamiento con autorreparación con el programador EventBridge.