Porque é que este pedido HTTP não está a funcionar na AWS Lambda?
o meu código de contacto é:
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);
}
E vejo as seguintes 4 linhas nos meus registos 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
Eu esperaria outro. fila ali dentro:
2015-02-11 07:38:06 UTC eb19c89d-b1c0-11e4-bceb-d310b88d37e2 Got response: 302
Mas isso desapareceu. Se estou a usar a parte essencial sem o invólucro do controlador no nó da minha máquina local, o código funciona como esperado.
o inputfile.txt
que estou a usar é para a chamada invoke-async
é esta:
{
"url":"http://www.google.com"
}
Parece que a parte do código que faz o pedido é ignorada por completo. Comecei com o pedido lib e voltei a usar o plain http
para criar um exemplo mínimo. Eu também tentei solicitar um URL de um serviço que eu controlo para verificar os registos e não há pedidos a chegar.
Estou completamente perplexo. existe alguma razão para o Node e / ou o AWS Lambda não executar o pedido HTTP?
8 answers
([11]} Para aqueles que encontram nodejs pela primeira vez em Lambda, um comum
erro é esquecer que os callbacks executam assíncronamente e chamando
context.done()
no manipulador original quando realmente querias esperar
para outro callback (como um S3.Operação de colocação) para completar, forçando
a função de terminar com seu trabalho incompleto.
Eu estava chamando context.done
Caminho antes de qualquer chamada para o pedido ser disparada, causando a interrupção da minha função antes do tempo.
O código de trabalho é 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);
}
Update: starting 2017 AWS has depreciated the old Nodejs 0.10 and only the newer 4.3 run-time is now available (old functions should be updated). Este tempo de execução introduziu algumas alterações na função de handler. O novo manipulador tem agora 3 parâmetros.
function(event, context, callback)
Embora ainda encontres o succeed
, done
e fail
na parâmetro de contexto, AWS sugere usar a função callback
em vez disso ou null
é devolvido por padrão.
callback(new Error('failure')) // to return error
callback(null, 'success msg') // to return ok
A documentação Completa pode ser encontrada em http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html
Exemplo simples de trabalho do pedido Http usando o nó.
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();
});
}
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();
};
Enfrentei este problema no nó 10.X version. abaixo está o meu código de trabalho.
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();
};
Eu tive o mesmo problema e então eu percebi que a programação em NodeJS é realmente diferente de Python ou Java como seu baseado em JavaScript. Vou tentar usar conceitos simples como pode haver algumas pessoas novas que estariam interessadas ou podem vir a esta questão.
Vamos ver o seguinte 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)
}
Sempre que você faz uma chamada para um método em http package (1) , ele é criado como evento e este evento recebe-o evento separado. A função' get '(2) é realmente o ponto de partida deste evento separado.
Agora, a função at (3) estará executando em um evento separado, e seu código vai continuar executando caminho e vai diretamente Saltar para (4) e terminá-lo, porque não há mais nada a fazer.
[[3]} mas o evento disparado em (2) ainda está em execução em algum lugar e vai levar seu próprio tempo doce para terminar. Muito bizarro, certo ?. Não, Não é. É assim que o NodeJS funciona e é muito importante que envolvas a tua cabeça nisto. conceito. Este é o lugar onde JavaScript promete vir para ajudar.Você pode ler mais sobre as promessas de JavaScript aqui. Em poucas palavras, você precisa de uma promessa JavaScript para manter a execução de código inline e não vai desovar novos / threads extra.
A maioria dos pacotes NodeJS comuns têm uma versão prometida de sua API disponível, mas existem outras abordagens como BlueBirdJS que abordam o problema similar.
O código que você tinha escrito acima pode ser vagamente reescrito como segue.
'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);
};
por favor, note que o código acima não vai funcionar diretamente se você vai importá-lo na AWS Lambda. Para Lambda, você vai precisar empacotar os módulos com a base de código também.
Eu encontrei muitos posts em toda a web sobre as várias maneiras de fazer o pedido, mas nenhum que realmente mostram como processar a resposta sincronicamente na AWS Lambda.
Aqui está uma função lambda Node 6.10.3 que usa um pedido https, recolhe e devolve o corpo inteiro da resposta, e passa o controle para uma função não listada processBody
com os resultados. Acredito que http e https são permutáveis neste código.
Estou a usar o módulo de utilidadesasync , que é é mais fácil de entender para novatos. Você vai precisar empurrar isso para a sua pilha AWS para usá-lo (eu recomendo o framework ([11]}sem servidor ).
Note que os dados voltam em pedaços, que são recolhidos em uma variável global, e finalmente o callback é chamado quando os dados têm 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);
});
}
};
A arquitectura da AWS Lambda
É um micro-serviço. Rodando dentro do EC2 com Amazon Linux AMI (versão 3.14.26-24.46.amzn1.x86_64) e corre com o nó.js. A memória pode ser beetwen 128mb e 1gb. Quando a fonte de dados despoleta o evento, os detalhes são passados a uma função Lambda como parâmetros. O que aconteceu? A "AWS Lambda run" está dentro de um recipiente, e a o código é enviado diretamente para este container com pacotes ou módulos. Por exemplo, nós nunca podemos fazer SSH para a máquina linux executando sua função lambda. As únicas coisas que podemos monitorar são os registros, com CloudWatchLogs e a exceção que veio do tempo de execução. A AWS trata do lançamento e elimina os contentores por nós, e apenas executa o código. Então, mesmo que você use require ('http'), não vai funcionar, porque o lugar onde este código é executado, não foi feito para isso.