tutorial nodejs node funcion exports ejemplo crear con aws node.js amazon-web-services httprequest aws-lambda

node.js - nodejs - ¿Por qué esta solicitud HTTP no funciona en AWS Lambda?



lambda node js (6)

Estoy comenzando con AWS Lambda y estoy tratando de solicitar un servicio externo de mi función de controlador. Según esta respuesta , las solicitudes HTTP deberían funcionar bien, y no he encontrado ninguna documentación que diga lo contrario. (De hecho, la gente ha publicado código que usa la API Twilio para enviar SMS ).

Mi código de controlador es:

var http = require(''http''); exports.handler = function(event, context) { console.log(''start request to '' + event.url) http.get(event.url, function(res) { console.log("Got response: " + res.statusCode); }).on(''error'', function(e) { console.log("Got error: " + e.message); }); console.log(''end request to '' + event.url) context.done(null); }

y veo las siguientes 4 líneas en mis registros de CloudWatch:

2015-02-11 07:38:06 UTC START RequestId: eb19c89d-b1c0-11e4-bceb-d310b88d37e2 2015-02-11 07:38:06 UTC eb19c89d-b1c0-11e4-bceb-d310b88d37e2 start request to http://www.google.com 2015-02-11 07:38:06 UTC eb19c89d-b1c0-11e4-bceb-d310b88d37e2 end request to http://www.google.com 2015-02-11 07:38:06 UTC END RequestId: eb19c89d-b1c0-11e4-bceb-d310b88d37e2

Esperaría otra línea allí:

2015-02-11 07:38:06 UTC eb19c89d-b1c0-11e4-bceb-d310b88d37e2 Got response: 302

Pero eso falta. Si estoy usando la parte esencial sin el contenedor del controlador en el nodo de mi máquina local, el código funciona como se esperaba.

El inputfile.txt que estoy usando es para la llamada invoke-async es este:

{ "url":"http://www.google.com" }

Parece que la parte del código del controlador que hace la solicitud se omite por completo. Comencé con la solicitud lib y volví a usar http simple para crear un ejemplo mínimo. También he intentado solicitar una URL de un servicio que controlo para verificar los registros y no hay solicitudes entrantes.

Estoy totalmente perplejo. ¿Hay alguna razón por la que Node y / o AWS Lambda no ejecuten la solicitud HTTP?


Ejemplo de trabajo simple de solicitud Http usando el nodo.

const http = require(''https'') exports.handler = async (event) => { return httprequest().then((data) => { const response = { statusCode: 200, body: JSON.stringify(data), }; return response; }); }; function httprequest() { return new Promise((resolve, reject) => { const options = { host: ''jsonplaceholder.typicode.com'', path: ''/todos'', port: 443, method: ''GET'' }; const req = http.request(options, (res) => { if (res.statusCode < 200 || res.statusCode >= 300) { return reject(new Error(''statusCode='' + res.statusCode)); } var body = []; res.on(''data'', function(chunk) { body.push(chunk); }); res.on(''end'', function() { try { body = JSON.parse(Buffer.concat(body).toString()); } catch(e) { reject(e); } resolve(body); }); }); req.on(''error'', (e) => { reject(e.message); }); // send the request req.end(); }); }


He encontrado muchas publicaciones en la web sobre las diversas formas de hacer la solicitud, pero ninguna que realmente muestre cómo procesar la respuesta de forma sincrónica en AWS Lambda.

Aquí hay una función lambda del Nodo 6.10.3 que usa una solicitud https, recopila y devuelve el cuerpo completo de la respuesta, y pasa el control a una función no processBody con los resultados. Creo que http y https son intercambiables en este código.

Estoy usando el módulo de utilidad asíncrono , que es más fácil de entender para los novatos. Tendrá que llevar eso a su pila de AWS para usarlo (recomiendo el marco sin servidor ).

Tenga en cuenta que los datos vuelven en fragmentos, que se recopilan en una variable global, y finalmente se llama a la devolución de llamada cuando los datos han end .

''use strict''; const async = require(''async''); const https = require(''https''); module.exports.handler = function (event, context, callback) { let body = ""; let countChunks = 0; async.waterfall([ requestDataFromFeed, // processBody, ], (err, result) => { if (err) { console.log(err); callback(err); } else { const message = "Success"; console.log(result.body); callback(null, message); } }); function requestDataFromFeed(callback) { const url = ''https://put-your-feed-here.com''; console.log(`Sending GET request to ${url}`); https.get(url, (response) => { console.log(''statusCode:'', response.statusCode); response.on(''data'', (chunk) => { countChunks++; body += chunk; }); response.on(''end'', () => { const result = { countChunks: countChunks, body: body }; callback(null, result); }); }).on(''error'', (err) => { console.log(err); callback(err); }); } };


Por supuesto, estaba malinterpretando el problema. Como AWS lo expresó :

Para aquellos que se encuentran con nodejs por primera vez en Lambda, un error común es olvidar que las devoluciones de llamada se ejecutan de forma asincrónica y llamar a context.done() en el controlador original cuando realmente quería esperar otra devolución de llamada (como una operación S3.PUT) para completa, obligando a la función a terminar con su trabajo incompleto.

Estaba llamando a context.done mucho antes de que se context.done cualquier devolución de llamada para la solicitud, lo que provocó la finalización de mi función antes de tiempo.

El código de trabajo es este:

var http = require(''http''); exports.handler = function(event, context) { console.log(''start request to '' + event.url) http.get(event.url, function(res) { console.log("Got response: " + res.statusCode); context.succeed(); }).on(''error'', function(e) { console.log("Got error: " + e.message); context.done(null, ''FAILURE''); }); console.log(''end request to '' + event.url); }

Actualización: a partir de 2017, AWS ha dejado de usar el antiguo Nodejs 0.10 y solo está disponible el nuevo tiempo de ejecución 4.3 (las funciones antiguas deberían actualizarse). Este tiempo de ejecución introdujo algunos cambios en la función del controlador. El nuevo controlador tiene ahora 3 parámetros.

function(event, context, callback)

Aunque todavía encontrará el succeed , el done y el fail en el parámetro de contexto, AWS sugiere usar la función de callback lugar o se devuelve null forma predeterminada.

callback(new Error(''failure'')) // to return error callback(null, ''success msg'') // to return ok

La documentación completa se puede encontrar en http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html


Sí, de hecho, hay muchas razones por las que puede acceder a AWS Lambda like y HTTP Endpoint.

La arquitectura de AWS Lambda

Es un microservicio. Ejecutando dentro de EC2 con Amazon Linux AMI (Versión 3.14.26–24.46.amzn1.x86_64) y se ejecuta con Node.js. La memoria puede estar entre 128mb y 1gb. Cuando la fuente de datos desencadena el evento, los detalles se pasan a una función Lambda como parámetro.

¿Qué ocurre?

AWS Lambda se ejecuta dentro de un contenedor, y el código se carga directamente a este contenedor con paquetes o módulos. Por ejemplo, NUNCA podemos hacer SSH para la máquina Linux que ejecuta su función lambda. Lo único que podemos monitorear son los registros, con CloudWatchLogs y la excepción que vino del tiempo de ejecución.

AWS se encarga de lanzar y finalizar los contenedores por nosotros, y simplemente ejecuta el código. Entonces, incluso si usa require (''http''), no va a funcionar, porque el lugar donde se ejecuta este código no fue hecho para esto.


Sí, la respuesta es perfecta. Solo mostraré mi código de trabajo ... Tuve el context.succeed (''Blah''); línea justo después de reqPost.end (); línea. Moverlo a donde muestro a continuación resolvió todo.

console.log(''GW1''); var https = require(''https''); exports.handler = function(event, context) { var body=''''; var jsonObject = JSON.stringify(event); // the post options var optionspost = { host: ''the_host'', path: ''/the_path'', method: ''POST'', headers: { ''Content-Type'': ''application/json'', } }; var reqPost = https.request(optionspost, function(res) { console.log("statusCode: ", res.statusCode); res.on(''data'', function (chunk) { body += chunk; }); context.succeed(''Blah''); }); reqPost.write(jsonObject); reqPost.end(); };


Tuve el mismo problema y luego me di cuenta de que la programación en NodeJS es realmente diferente de Python o Java, ya que se basa en JavaScript. Trataré de usar conceptos simples, ya que puede haber algunas personas nuevas que estén interesadas o puedan llegar a esta pregunta.

Veamos el siguiente código:

var http = require(''http''); // (1) exports.handler = function(event, context) { console.log(''start request to '' + event.url) http.get(event.url, // (2) function(res) { //(3) console.log("Got response: " + res.statusCode); context.succeed(); }).on(''error'', function(e) { console.log("Got error: " + e.message); context.done(null, ''FAILURE''); }); console.log(''end request to '' + event.url); //(4) }

Cada vez que realiza una llamada a un método en el paquete http (1), se crea como evento y este evento lo obtiene por separado. La función ''get'' (2) es en realidad el punto de partida de este evento separado.

Ahora, la función en (3) se ejecutará en un evento separado, y su código continuará ejecutando la ruta y saltará directamente a (4) y terminará, porque no hay nada más que hacer.

Pero el evento disparado a (2) todavía se está ejecutando en algún lugar y tomará su dulce tiempo para terminar. Bastante extraño, ¿verdad? Bueno, no, no lo es. Así es como funciona NodeJS y es bastante importante que sepas este concepto. Este es el lugar donde JavaScript Promises viene a ayudar.

Puede leer más sobre las promesas de JavaScript here . En pocas palabras, necesitaría una Promesa de JavaScript para mantener la ejecución del código en línea y no generará hilos nuevos / adicionales.

La mayoría de los paquetes comunes de NodeJS tienen una versión Promesa de su API disponible, pero hay otros enfoques como BlueBirdJS que abordan el problema similar.

El código que había escrito anteriormente puede reescribirse libremente de la siguiente manera.

''use strict''; console.log(''Loading function''); var rp = require(''request-promise''); exports.handler = (event, context, callback) => { var options = { uri: ''https://httpbin.org/ip'', method: ''POST'', body: { }, json: true }; rp(options).then(function (parsedBody) { console.log(parsedBody); }) .catch(function (err) { // POST failed... console.log(err); }); context.done(null); };

Tenga en cuenta que el código anterior no funcionará directamente si lo importará en AWS Lambda. Para Lambda, también deberá empaquetar los módulos con la base de código.