Estoy comenzando con AWS Lambda y estoy intentando solicitar un servicio externo desde mi función de controlador. De acuerdo con esta respuesta , las solicitudes HTTP deberían funcionar bien y no he encontrado ninguna documentación que indique lo contrario. (De hecho, la gente ha publicado código que usa la API de 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 invoke-async
llamada es este:
{
"url":"http://www.google.com"
}
Parece que la parte del código del controlador que realiza la solicitud se omite por completo. Comencé con la biblioteca de solicitudes y volví a usar plain http
para crear un ejemplo mínimo. También intenté solicitar una URL de un servicio que controlo para verificar los registros y no ingresaron solicitudes.
Estoy totalmente perplejo. ¿Hay alguna razón por la que Node y / o AWS Lambda no ejecutarían la solicitud HTTP?
Respuestas:
Por supuesto, estaba entendiendo mal el problema. Como lo expresaron los propios AWS :
Estaba llamando
context.done
mucho antes de que se disparara cualquier devolución de llamada para la solicitud, lo que provocó la terminació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 desaprobado el antiguo Nodejs 0.10 y ahora solo está disponible el tiempo de ejecución 4.3 más reciente (las funciones antiguas deben 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 parámetro de contexto
succeed
,done
yfail
en el, AWS sugiere usar lacallback
función en su lugar onull
se devuelve de 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
fuente
context.done()
llamada debe trasladarse a las devoluciones de llamada (para casos de éxito y error).Ejemplo de trabajo simple de solicitud Http usando node.
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(); }); }
fuente
node-fetch
request
etc., no están disponibles en Lambda de forma predeterminada.Sí, la respuesta de Awendt es perfecta. Solo mostraré mi código de trabajo ... Tenía 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(); };
fuente
Me enfrenté a este problema en la versión Node 10.X. a continuación está mi código de trabajo.
const https = require('https'); exports.handler = (event,context,callback) => { let body=''; let jsonObject = JSON.stringify(event); // the post options var optionspost = { host: 'example.com', path: '/api/mypath', method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'blah blah', } }; let reqPost = https.request(optionspost, function(res) { console.log("statusCode: ", res.statusCode); res.on('data', function (chunk) { body += chunk; }); res.on('end', function () { console.log("Result", body.toString()); context.succeed("Sucess") }); res.on('error', function () { console.log("Result Error", body.toString()); context.done(null, 'FAILURE'); }); }); reqPost.write(jsonObject); reqPost.end(); };
fuente
Tuve el mismo problema y luego me di cuenta de que la programación en NodeJS es en realidad diferente a Python o Java, ya que está basada 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) }
Siempre 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 'obtener' (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 en (2) todavía se está ejecutando en algún lugar y tomará su propio tiempo para terminar. Bastante extraño, ¿verdad? Bueno, no, no lo es. Así es como funciona NodeJS y es muy importante que entiendas este concepto. Este es el lugar donde JavaScript Promises viene a ayudar.
Puede leer más sobre JavaScript Promises aquí . En pocas palabras, necesitaría una promesa de JavaScript para mantener la ejecución del código en línea y no generará subprocesos nuevos o adicionales.
La mayoría de los paquetes comunes de NodeJS tienen disponible una versión prometida de su API, pero existen otros enfoques como BlueBirdJS que abordan un problema similar.
El código que había escrito anteriormente se puede reescribir 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 va a importar en AWS Lambda. Para Lambda, también deberá empaquetar los módulos con el código base.
fuente
context.done()
llamada a unfinally
método encadenado .Encontré muchas publicaciones en la web sobre las diversas formas de realizar la solicitud, pero ninguna que realmente muestre cómo procesar la respuesta sincrónicamente 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 listada
processBody
con los resultados. Creo que http y https son intercambiables en este código.Estoy usando el módulo de utilidad async , que es más fácil de entender para los principiantes. Deberá enviarlo a su AWS Stack 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 se han
end
ed.'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); }); } };
fuente
Agregue el código anterior en la puerta de enlace API en GET-Integration Request> sección de mapeo.
fuente
Sí, de hecho, existen muchas razones por las que puede acceder a AWS Lambda like y HTTP Endpoint.
La arquitectura de AWS Lambda
Es un microservicio. Se ejecuta 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 ser de entre 128 MB y 1 GB. Cuando la fuente de datos activa 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 en 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 del lanzamiento y la terminación de los contenedores por nosotros, y simplemente ejecuta el código. Entonces, incluso si usa require ('http'), no funcionará, porque el lugar donde se ejecuta este código no fue creado para esto.
fuente