Estou começando a usar o AWS Lambda e tentando solicitar um serviço externo de minha função de manipulador. De acordo com esta resposta , as solicitações HTTP devem funcionar bem e não encontrei nenhuma documentação que diga o contrário. (Na verdade, as pessoas postaram códigos que usam a API do Twilio para enviar SMS .)
Meu código de manipulador é:
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 em meus logs do 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 outra linha lá:
2015-02-11 07:38:06 UTC eb19c89d-b1c0-11e4-bceb-d310b88d37e2 Got response: 302
mas está faltando. Se eu estiver usando a parte essencial sem o wrapper do manipulador no nó em minha máquina local, o código funcionará conforme o esperado.
O inputfile.txt
que estou usando é para a invoke-async
chamada é este:
{
"url":"http://www.google.com"
}
Parece que a parte do código do manipulador que faz a solicitação foi totalmente ignorada. Comecei com o request lib e voltei a usar o plain http
para criar um exemplo mínimo. Também tentei solicitar uma URL de um serviço que eu controlo para verificar os logs e não há solicitações entrando.
Estou totalmente perplexo. Existe algum motivo pelo qual o Node e / ou AWS Lambda não executaria a solicitação HTTP?
Respostas:
Claro, eu estava entendendo mal o problema. Como a própria AWS colocou :
Eu estava chamando
context.done
muito antes de qualquer retorno de chamada para a solicitação ser acionado, causando o encerramento 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); }
Atualização: a partir de 2017, a AWS suspendeu o uso do Nodejs 0.10 antigo e apenas o tempo de execução 4.3 mais recente está disponível (as funções antigas devem ser atualizadas). Este tempo de execução introduziu algumas mudanças na função do manipulador. O novo manipulador agora tem 3 parâmetros.
function(event, context, callback)
Embora você ainda encontre o parâmetro de contexto
succeed
,done
efail
no, a AWS sugere usar acallback
função em seu lugar ou elanull
é retornada 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
fonte
context.done()
chamada precisa ser movida para os retornos de chamada (para casos de sucesso e erro).Exemplo de trabalho simples de solicitação 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(); }); }
fonte
node-fetch
request
etc, não estão disponíveis no Lambda por padrão.Sim, uma resposta awendt é perfeita. Vou apenas mostrar meu código de trabalho ... Tive o context.succeed ('Blah'); linha logo após o reqPost.end (); linha. Movê-lo para onde mostro abaixo resolveu tudo.
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(); };
fonte
Enfrentei esse problema na versão Node 10.X. abaixo está 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(); };
fonte
Eu tive o mesmo problema e então percebi que a programação em NodeJS é realmente diferente de Python ou Java, pois é baseada em JavaScript. Tentarei usar conceitos simples, pois pode haver algumas pessoas novas que estariam interessadas ou podem fazer essa pergunta.
Vejamos 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 no pacote http (1), ele é criado como evento e este evento o obtém como evento separado. A função 'get' (2) é na verdade o ponto de partida desse evento separado.
Agora, a função em (3) será executada em um evento separado, e seu código continuará executando o caminho e irá direto para (4) e finalizará, porque não há mais nada a fazer.
Mas o evento disparado em (2) ainda está sendo executado em algum lugar e levará seu próprio tempo para terminar. Muito bizarro, certo? Bem, não, não é. É assim que o NodeJS funciona e é muito importante que você entenda esse conceito. Este é o lugar onde JavaScript Promises vem para ajudar.
Você pode ler mais sobre as Promessas de JavaScript aqui . Resumindo, você precisaria de uma promessa de JavaScript para manter a execução do código embutido e não gerará threads novas / extras.
A maioria dos pacotes NodeJS comuns tem uma versão prometida de sua API disponível, mas existem outras abordagens, como BlueBirdJS, que abordam o problema semelhante.
O código que você escreveu acima pode ser reescrito livremente da seguinte maneira.
'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); };
Observe que o código acima não funcionará diretamente se você importá-lo no AWS Lambda. Para Lambda, você precisará empacotar os módulos com a base de código também.
fonte
context.done()
chamada para umfinally
método encadeado .Eu encontrei muitas postagens na web sobre as várias maneiras de fazer a solicitação, mas nenhuma que realmente mostre como processar a resposta de forma síncrona no AWS Lambda.
Aqui está uma função lambda do Nó 6.10.3 que usa uma solicitação https, coleta e retorna o corpo completo da resposta e passa o controle para uma função não listada
processBody
com os resultados. Acredito que http e https são intercambiáveis neste código.Estou usando o módulo de utilitário assíncrono , que é mais fácil de entender para iniciantes. Você precisará enviar isso ao seu AWS Stack para usá-lo (eu recomendo a estrutura sem servidor ).
Observe que os dados voltam em blocos, que são reunidos em uma variável global e, finalmente, o retorno de chamada é chamado quando os dados são
end
editados.'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); }); } };
fonte
Adicione o código acima no gateway de API em Solicitação de integração GET> seção de mapeamento.
fonte
Sim, existem muitos motivos pelos quais você pode acessar o AWS Lambda like e o HTTP Endpoint.
A arquitetura do AWS Lambda
É um microsserviço. Executado dentro do EC2 com Amazon Linux AMI (versão 3.14.26–24.46.amzn1.x86_64) e executado com Node.js. A memória pode ser entre 128 MB e 1 GB. Quando a fonte de dados dispara o evento, os detalhes são passados para uma função Lambda como parâmetro.
O que acontece?
O AWS Lambda é executado dentro de um contêiner e o código é carregado diretamente para esse contêiner com pacotes ou módulos. Por exemplo, NUNCA podemos fazer SSH para a máquina Linux executando sua função lambda. As únicas coisas que podemos monitorar são os logs, com CloudWatchLogs e a exceção que veio do tempo de execução.
A AWS se encarrega de iniciar e encerrar os contêineres para nós, e apenas executa o código. Então, mesmo que você use require ('http'), não vai funcionar, pois o local onde esse código roda não foi feito para isso.
fonte