Si eres como yo, un fanático de la programación basada en eventos, querrás seguir leyendo. Hoy analizaremos la activación de funciones de AWS Lambda a partir de mensajes de AWS SNS . Ya he cubierto algunos temas interesantes relacionados con las arquitecturas sin servidor y AWS, pero nada como esto todavía. Excava y prepárate. Vamos.
Nota *: todo el código de este tutorial ya está en GitHub si desea ver el resultado final de inmediato.*
Nuestro enfoque estará únicamente en los pasos para crear los componentes de infraestructura que necesitará nuestra aplicación. El código en sí solo imitará el comportamiento de un cálculo complejo aleatorio. Elegí una función recursiva que calcula el factorial del número que se le pasa. ¡Aquí hay un buen diagrama, porque los diagramas son increíbles, por supuesto!
La función init
es la única función expuesta, que está conectada a API Gateway . Toma un solo parámetro de number
que valida, si tiene éxito, publica un tema de SNS y envía el valor del number
.
El tema de SNS activará una segunda función llamada calculate
. Esta función realizará el cálculo y cerrará la sesión del resultado en la consola. Esto imita una pesada tarea computacional en segundo plano, como el procesamiento de datos, la manipulación de imágenes o los cálculos de aprendizaje automático.
Si la función de calculate
falla, el tema SNS de cola de mensajes fallidos recibirá un mensaje y activará la función de error
.
Cada función invocada de forma asincrónica volverá a intentar su ejecución dos veces si falla. Usar la cola de mensajes fallidos como grupo para sus registros de errores es un caso de uso inteligente.
Ahora se pregunta, ¿por qué toda la complicación con SNS en lugar de simplemente invocar la segunda función lambda desde la primera con la API de invocación de Lambda ?
En primer lugar, es un gran antipatrón para flujos de trabajo asincrónicos, que es nuestro caso. De lo contrario, está bien si necesita la respuesta de la segunda función lambda de inmediato. Otro problema es alcanzar los límites de concurrencia de Lambda bastante rápido. Puede provocar la pérdida de invocaciones y la eliminación de datos. Enviar sus datos a través de un servicio de publicación/suscripción como SNS o una cola como SQS garantizará la integridad de los datos.
¿Tiene sentido ahora? Dulce, hablemos un poco más sobre SNS.
Antes de comenzar a codificar, debemos cubrir los conceptos básicos. Sabemos qué es AWS Lambda, pero ¿qué pasa con SNS? Los documentos de AWS son bastante sencillos.
Amazon Simple Notification Service (SNS) es un servicio de notificaciones móviles y de mensajería pub/sub flexible y completamente administrado para coordinar la entrega de mensajes a los clientes y puntos finales suscritos.
-- Documentos de AWS
En inglés, esto significa que es una forma de enviar notificaciones entre servicios sobre la base de editor/suscriptor. Un servicio publica algunos datos sobre un tema y los envía a lo largo de su camino. SNS luego lo canalizará a todos los suscriptores de ese tema en particular. El enfoque clave está en el tema aquí, verá por qué un poco más abajo.
Lo primero que hay que hacer, como siempre, es configurar el proyecto e instalar las dependencias.
Mi herramienta preferida de desarrollo e implementación para aplicaciones sin servidor es Serverless Framework . Vamos a seguir adelante e instalarlo.
$ npm i -g serverless
Nota: si está utilizando Linux, es posible que deba ejecutar el comando como sudo.
Una vez instalado globalmente en su máquina, los comandos estarán disponibles para usted desde cualquier parte de la terminal. Pero para que se comunique con su cuenta de AWS, debe configurar un usuario de IAM. Salta aquí para ver la explicación , luego regresa y ejecuta el comando a continuación, con las teclas provistas.
$ serverless config credentials\ --provider aws\ --key xxxxxxxxxxxxxx\ --secret xxxxxxxxxxxxxx
Ahora su instalación sin servidor sabe a qué cuenta conectarse cuando ejecuta cualquier comando de terminal. Entremos y veámoslo en acción.
Cree un nuevo directorio para albergar sus servicios de aplicaciones sin servidor. Enciende una terminal allí. Ahora está listo para crear un nuevo servicio.
¿Qué es un servicio? Es como un proyecto. Es donde define las funciones de AWS Lambda, los eventos que las desencadenan y los recursos de infraestructura de AWS que requieren, incluido SNS, que agregaremos hoy, todo en un archivo llamado serverless.yml .
De vuelta en su tipo de terminal:
$ serverless create --template aws-nodejs\ --path lambda-sns-dlq-error-handling
El comando crear creará un nuevo servicio . ¡Qué sorpresa! También elegimos un tiempo de ejecución para la función. Esto se llama la plantilla . Pasar aws-nodejs
establecerá el tiempo de ejecución en Node.js. Justo lo que queremos. La ruta creará una carpeta para el servicio.
Abra la carpeta lambda-sns-dlq-error-handling con su editor de código favorito. Debería haber tres archivos allí, pero por ahora, solo nos centraremos en serverless.yml . Contiene todos los ajustes de configuración de este servicio. Aquí se especifican tanto los ajustes de configuración generales como los ajustes por función. Su serverless.yml estará lleno de código repetitivo y comentarios. Siéntase libre de eliminarlo todo y pegar esto.
service: lambda-sns-dlq-error-handling plugins: - serverless-pseudo-parameters provider: name: aws runtime: nodejs8.10 stage: dev region: eu-central-1 memorySize: 128 environment: accountId: '#{AWS::AccountId}' region: '#{AWS::Region}' iamRoleStatements: - Effect: "Allow" Resource: "*" Action: - "sns:*" functions: init: handler: init.handler events: - http: path: init method: post cors: true calculate: handler: calculate.handler events: - sns: calculate-topic # created immediately onError: arn:aws:sns:#{AWS::Region}:#{AWS::AccountId}:dlq-topic error: handler: error.handler events: - sns: dlq-topic # created immediately
Analicemos lo que está pasando aquí. Consulte la sección de functions
. Hay tres funciones aquí. De arriba a abajo son init
, calculate
y error
. La función init
se activará mediante una solicitud HTTP simple, que invocamos a través de API Gateway. Territorio familiar para nosotros.
Sin embargo, las funciones de calculate
y error
son activadas por temas de SNS. Lo que significa que tendremos lógica en la función de init
que publicará mensajes en un tema llamado calculate-topic
mientras la función de calculate
está suscrita al mismo tema.
Continuando, la función de error
está suscrita al dlq-topic
mientras que la función de calculate
publicará mensajes en este tema si falla, como puede ver con la propiedad onError
. Ahora las cosas tienen sentido, ¿verdad?
Tome nota mentalmente, una vez que agregue los temas SNS como eventos para sus funciones, los recursos se crearán automáticamente una vez que implemente el servicio.
Qué más, eche un vistazo a iamRoleStatements
, especifican que nuestras funciones tienen permiso para activarse y ser invocadas por temas de SNS. Mientras que el complemento serverless-pseudo-parameters
nos permite hacer referencia a nuestro AccountId
y Region
con la sintaxis de CloudFormation, lo que hace que sea mucho más fácil mantener la coherencia de nuestros SNS ARN en todos los recursos.
Por suerte, esta parte será corta. Solo un paquete para instalar. Primero, inicialice npm y luego puede instalar serverless-pseudo-parameters
.
$ npm init -y && npm i serverless-pseudo-parameters
Eso servirá.
Con todas las cosas consideradas, el proceso de configuración fue bastante simple. El código que escribiremos ahora es igual de sencillo. Nada extraordinario que ver, lamento decepcionarte.
Mantengamos las tres funciones en archivos separados, para mantenerlo simple. En primer lugar, cree un archivo init.js y pegue este fragmento.
// init.js const aws = require('aws-sdk') const sns = new aws.SNS({ region: 'eu-central-1' }) function generateResponse (code, payload) { console.log(payload) return { statusCode: code, body: JSON.stringify(payload) } } function generateError (code, err) { console.error(err) return generateResponse(code, { message: err.message }) } async function publishSnsTopic (data) { const params = { Message: JSON.stringify(data), TopicArn: `arn:aws:sns:${process.env.region}:${process.env.accountId}:calculate-topic` } return sns.publish(params).promise() } module.exports.handler = async (event) => { const data = JSON.parse(event.body) if (typeof data.number !== 'number') { return generateError(400, new Error('Invalid number.')) } try { const metadata = await publishSnsTopic(data) return generateResponse(200, { message: 'Successfully added the calculation.', data: metadata }) } catch (err) { return generateError(500, new Error('Couldn\'t add the calculation due to an internal error.')) } }
Tenemos algunas funciones auxiliares y la función lambda exportada en la parte inferior. ¿Que está pasando aqui? La lambda valida la entrada y publica algunos datos en el tema de SNS calculate-topic
. Eso es todo lo que está haciendo esta función. El calculate-topic
tema de cálculo activará la función de calculate
lambda. Agreguemos eso ahora.
Cree un archivo y asígnele el nombre calcule.js . Pegue este fragmento.
// calculate.js module.exports.handler = async (event) => { const { number } = JSON.parse(event.Records[0].Sns.Message) const factorial = (x) => x === 0 ? 1 : x * factorial(x - 1) const result = factorial(number) console.log(`The factorial of ${number} is ${result}.`) return result }
Como puede ver, esto es solo un cálculo factorial simple implementado con una función recursiva. Calculará el factorial del número que publicamos en el tema SNS desde la función init
.
Una nota importante aquí es que si la función de calculate
falla un total de tres veces, publicará mensajes en el tema SNS de Dead Letter Queue que especificamos con la propiedad onError
en el archivo serverless.yml . La cola de mensajes fallidos activará la función de error
. Vamos a crearlo ahora para que pueda cerrar la sesión de errores en CloudWatch. Cree un archivo error.js y pegue estas líneas.
// error.js module.exports.handler = async (event) => { console.error(event) }
Por ahora, esto servirá. Sin embargo, lo ideal sería tener un registro estructurado con información detallada sobre todo lo que sucede. Ese es un tema para otro artículo.
Aquí viene la parte fácil. Implementar la API es tan simple como ejecutar un comando.
$ serverless deploy\ ![lambda deploy](https://cdn.hackernoon.com/images/cky-9-uq-08500-u-80-bs-6-b-31-s-7-jsp.jpg)
Puede ver que el punto final se registra en la consola. Allí es donde enviarás tus solicitudes.
La forma más sencilla de probar una API es con CURL. Vamos a crear un comando CURL simple y enviar una carga JSON a nuestro punto final.
$ curl -H "Content-Type: application/json"\ -d '{"number":1000}'\ https://<id>.execute-api.eu-central-1.amazonaws.com/dev/init
Si todo funciona como debería, el resultado del cálculo se registrará en CloudWatch. Si no, bueno, entonces no tienes suerte.
Después de llegar al punto final varias veces con un par de valores diferentes, este es el resultado. La función init
funciona como se esperaba.
Pero, lo que realmente nos interesa es la función de calculate
. Esto es lo que parece cuando tiene éxito.
Cuando falla, especificará un bloqueo y mostrará los registros de errores.
Después de dos reintentos, enviará un mensaje a Dead Letter Queue y activará la función de error
.
¡Dulce! Hemos probado todos los diferentes escenarios. Espero que esto aclare las cosas un poco.
Eso es todo amigos. Cubrimos la creación de activadores de SNS para Lambda y, al mismo tiempo, implementamos una cola de mensajes fallidos para detectar errores de invocaciones fallidas. El uso sin servidor para varios cálculos intermitentes es un caso de uso válido que solo crecerá en popularidad en el futuro.
No hay servidores de los que deba preocuparse, y solo paga por el tiempo que se ejecuta. Simplemente implemente el código y tenga la seguridad de que funcionará.
Nuevamente, aquí está el repositorio de GitHub , si desea echar un vistazo al código. Puede actuar como un iniciador para sus propios casos de uso en los que necesita mensajes SNS que activen las funciones de Lambda. Dale una estrella si te gusta y quieres que más personas lo vean en GitHub.
También publicado en: https://dashbird.io/blog/triggering-lambda-with-sns-messaging/