node.js - nodejs - node js lambda function example
Conexiones MongoDB de AWS Lambda (9)
Estoy buscando crear una API RESTful utilizando AWS Lambda / API Gateway conectada a una base de datos MongoDB. He leído que las conexiones a MongoDB son relativamente caras, por lo que es una buena práctica retener una conexión para su reutilización una vez establecida, en lugar de establecer nuevas conexiones para cada nueva consulta.
Esto es bastante sencillo para las aplicaciones normales, ya que puede establecer una conexión durante el inicio y volver a utilizarla durante la vida útil de las aplicaciones. Pero, dado que Lambda está diseñado para ser apátrida, mantener esta conexión parece ser menos directo.
Por lo tanto, me pregunto cuál sería la mejor manera de abordar este problema de conexión a la base de datos. ¿Estoy obligado a hacer nuevas conexiones cada vez que se invoca una función Lambda o hay una forma de agrupar / almacenar en caché estas conexiones para consultas más eficientes?
Gracias.
Además de guardar la conexión para su reutilización, aumente la asignación de memoria para la función lambda. AWS asigna CPU proporcionalmente a la asignación de memoria y cuando se cambia de 128MB a 1.5Gb, el tiempo de conexión cayó de 4s a 0.5s al conectarse a mongodb atlas.
Lea más aquí: https://aws.amazon.com/lambda/faqs/
Debes asumir que lambdas es apátrida, pero la realidad es que la mayoría de las veces el vm simplemente se congela y mantiene un cierto estado. Sería estúpido para Amazon generar un nuevo proceso para cada solicitud, por lo que a menudo reutilizan el mismo proceso y puede aprovecharlo para evitar conexiones complicadas.
Para evitar la conexión para cada solicitud (en los casos en que se reutiliza el proceso lambda):
Escriba el controlador suponiendo que el proceso se reutiliza para que se conecte a la base de datos y tenga el lamba reutilizar el conjunto de conexiones (la promesa de
db
devuelta porMongoClient.connect
).Para que la lambda no se cuelgue esperando a que cierre la conexión de db,
db.close()
, después de atender una solicitud, dígale que no espere un bucle de evento vacío.
Ejemplo:
var db = MongoClient.connect(MongoURI);
module.exports.targetingSpec = (event, context, callback) => {
context.callbackWaitsForEmptyEventLoop = false;
db.then((db) => {
// use db
});
};
De la documentación sobre context.callbackWaitsForEmptyEventLoop
:
callbackWaitsForEmptyEventLoop El valor predeterminado es verdadero. Esta propiedad solo es útil para modificar el comportamiento predeterminado de la devolución de llamada. De forma predeterminada, la devolución de llamada esperará hasta que el bucle de evento de tiempo de ejecución Node.js esté vacío antes de congelar el proceso y devolver los resultados a la persona que llama. Puede establecer esta propiedad en falso para solicitar que AWS Lambda congele el proceso poco después de que se llame a la devolución de llamada, incluso si hay eventos en el ciclo de evento. AWS Lambda congelará el proceso, cualquier dato de estado y los eventos en el ciclo de evento Node.js (cualquier evento restante en el ciclo de evento procesado cuando se llame a la función Lambda y si AWS Lambda elige usar el proceso congelado). Para obtener más información sobre la devolución de llamada, consulte Uso del parámetro de devolución de llamada.
Desafortunadamente, puede que tenga que crear su propia API RESTful para responder a las solicitudes de MongoDB hasta que AWS encuentre una. Hasta ahora solo tienen lo que necesitas para su propia Dynamo DB.
Ejecuté algunas pruebas ejecutando funciones Java Lambda conectadas a MongoDB Atlas.
Como ya se indicó en otros carteles, Amazon reutiliza las Instancias, sin embargo, estas pueden reciclarse y el comportamiento exacto no puede determinarse. Entonces uno podría terminar con conexiones obsoletas. Recolecto información cada 5 minutos y la paso a la función Lambda cada 5 minutos.
El Lambda básicamente hace:
- Construir o reutilizar la conexión
- Consulta un registro
- Escribir o actualizar un registro
- cerrar la conexión o dejarla abierta
La cantidad real de datos es bastante baja. Dependiendo de la hora del día, varía de 1 a 5 kB. Solo usé 128 MB.
Los Lambda corrieron en N.Virgina ya que este es el lugar donde está vinculado el nivel libre .
Al abrir y cerrar la conexión cada vez que la mayoría de las llamadas toman entre 4500 y 9000 ms. Al reutilizar la conexión, la mayoría de las llamadas se encuentran entre 300 y 900 ms. Al verificar la consola Atlas, el conteo de conexiones se mantiene estable. Para este caso, reutilizar la conexión vale la pena. Crear una conexión e incluso desconectarse de un conjunto de réplicas es bastante costoso con el controlador de Java.
Para una implementación a gran escala, uno debe ejecutar pruebas más exhaustivas.
Hice algunos análisis sobre cómo usar el conjunto de conexiones de MongoDB en AWS lambda y anoté mis observaciones en mi blog .
Debe tener en cuenta factores múltiples antes de intentar utilizar AWS lambda con el gateway de la API de Amazon como desencadenante. Factores como tiempo de espera de integración, arranque en frío de Lambda, qué hacer en caso de inactividad importa mucho.
Las funciones de Lambda son sin estado y asíncronas, y al usar el grupo de conexiones de la base de datos, le está agregando estado. Sin embargo, esto solo ayudará cuando se reutilicen los contenedores, lo que le permitirá ahorrar mucho tiempo.
La respuesta corta es sí, necesita crear una nueva conexión Y cerrarla antes de que termine la lambda.
La respuesta larga es en realidad durante mis pruebas puedes pasar tus conexiones de base de datos en tu controlador como eso (ejemplo de mysql porque eso es lo que tengo a mano), no puedes confiar en que esto tenga una conexión, así que revisa mi ejemplo a continuación, puede ser que una vez que su Lambda no se haya ejecutado durante años pierda el estado del controlador (arranque en frío), tengo que hacer más pruebas para averiguarlo, pero he notado si un Lambda está recibiendo mucho tráfico utilizando el siguiente ejemplo, no crea una nueva conexión.
// MySQL.database.js
import * as mysql from ''mysql''
export default mysql.createConnection({
host: ''mysql db instance address'',
user: ''MYSQL_USER'',
password: ''PASSWORD'',
database: ''SOMEDB'',
})
Luego, en su controlador, impórtelo y páselo a la lambda que se está ejecutando.
// handler.js
import MySQL from ''./MySQL.database.js''
const funcHandler = (func) => {
return (event, context, callback) => {
func(event, context, callback, MySQL)
}
}
const handler = {
someHandler: funcHandler(someHandler),
}
export default handler
Ahora en tu Lambda lo haces ...
export default (event, context, callback, MySQL) => {
context.callbackWaitsForEmptyEventLoop = false
// Check if their is a MySQL connection if not, then open one.
// Do ya thing, query away etc etc
callback(null, responder.success())
}
El ejemplo del respondedor se puede encontrar here. lo siento es ES5 porque ahí es donde se hizo la pregunta.
¡Espero que esto ayude!
Las funciones de AWS Lambda se deben definir como funciones sin estado, por lo que no pueden contener el estado como un grupo de conexiones.
Este problema también se planteó en esta publicación del foro de AWS . El 5 de octubre de 2015, el ingeniero de AWS, Sean, publicó que no debe abrir y cerrar la conexión en cada solicitud, creando un grupo en la inicialización del código, fuera del bloque del controlador. Pero dos días después, el mismo ingeniero publicó que no deberías hacer esto .
El problema es que no tienes control sobre el entorno de ejecución de Lambda. Sabemos que estos entornos (o contenedores) se reutilizan, como describe la publicación del blog de Tim Wagner . Pero la falta de control puede llevarlo a drenar todos sus recursos, como alcanzar un límite de conexión en su base de datos. Pero depende de ti.
En lugar de conectarse a MongoDB desde su función lambda, puede usar Restheart para acceder a la base de datos a través de HTTP. En cambio, RESTHeart mantiene el conjunto de conexiones a MongoDB. Recuerde que en lo que respecta al rendimiento, abrirá una nueva conexión HTTP a RESTHeart en cada solicitud, y no usará un grupo de conexiones HTTP, como podría hacer en una aplicación tradicional.
Me enfrenté al mismo problema hace algunas veces, pero lo he resuelto poniendo mi mongo en la misma cuenta de EC2. Creé un mongo DB en la misma cuenta de AWS EC2 donde reside mi función lambda.
Ahora puedo acceder a mi mongo desde la función lambda con IP privada.
Restheart es un servidor basado en REST que se ejecuta junto con MongoDB. Mapas de la mayoría de las operaciones CRUD en Mongo para GET, POST, etc., solicitudes con soporte extensible cuando necesita escribir un controlador personalizado (por ejemplo, geoNear especializado, consulta de geoSearch)