Por que esta solicitação HTTP não está funcionando no AWS Lambda?


90

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.txtque estou usando é para a invoke-asyncchamada é 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 httppara 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?


Acho que isso pode ser causado devido à falta de um user agent em sua solicitação HTTP.
— Ma'moon Al-Akash

4
No momento em que este artigo foi escrito, esta é atualmente a principal questão no fórum Lambda dos fóruns da AWS. Isso está me deixando louco e também um monte de outras pessoas.
— Nostradamus

@Nostradamus Agradeço qualquer feedback adicional, correções e votos positivos. Envie-os aqui ;-)
— awendt

1
Tentei de tudo, desde o exemplo Twillo até alguns exemplos padrão enviados com o pacote de exemplo do nó Alexa e também o método context.done (). O HTTP POST não está funcionando. É possível postar uma amostra completa do seu código de solicitação POST?
— chheplo

Respostas:


80

Claro, eu estava entendendo mal o problema. Como a própria AWS colocou :

Para aqueles que encontram nodejs pela primeira vez no Lambda, um erro comum é esquecer que callbacks são executados de forma assíncrona e chamar context.done()o manipulador original quando você realmente pretendia esperar que outro callback (como uma operação S3.PUT) fosse concluído, forçando a função terminar com seu trabalho incompleto.

Eu estava chamando context.donemuito 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, donee failno, a AWS sugere usar a callbackfunção em seu lugar ou ela nullé 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


4
então, como você faz o código do manipulador funcionar? Pelo que entendi, você precisa remover context.done () para que a função de retorno de chamada seja chamada. mas seu código ainda não funciona para mim. :(
— mabeiyi

3
A context.done()chamada precisa ser movida para os retornos de chamada (para casos de sucesso e erro).
— awendt

2
não tive seu problema ainda, mas é ótimo ter em mente enquanto eu continuo com lambda.
— David

alguma ideia sobre como posso invocar uma API em meu sistema local do Lambda?
— Amit Kumar Ghosh

2
adereços para atualizar uma questão de 2015 com atualizações de 2017!
— Ás

21

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();
    });
}

Obrigado por isso. Esta é a melhor resposta que vejo nesta página em 2019, agora que o Lambda está usando a sintaxe await.
— Taneem Tee de

3
Levei mais de uma hora para encontrar a melhor resposta, já que as bibliotecas, node-fetch requestetc, não estão disponíveis no Lambda por padrão.
— Alex C

Muito do código de amostra que existe parece estar quebrado agora. Este é um exemplo de código de trabalho em março de 2020, usando AWS Lambda com Node.js 12.x
— Muhammad Yussuf

Alguém pode explicar como fazer solicitações POST com dados dentro de funções lambda?
— Pavindu

11

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();
};

4

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();
};

3

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.


Sim, promessas! Embora eu considere mover a context.done()chamada para um finallymétodo encadeado .
— crftr

3

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 processBodycom 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 endeditados.

'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);
        });
    }
};

0

insira a descrição da imagem aqui

Adicione o código acima no gateway de API em Solicitação de integração GET> seção de mapeamento.


-14

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.


5
Você pode ter entendido mal o meu problema. Eu sei que o código Lambda está sendo executado em um contêiner e sei que não consigo acessar a máquina subjacente. Nem estou tentando entrar, meu código está tentando sair, ou seja, acessar pontos de extremidade externos e o Lambda pode fazer isso muito bem. O problema era algo totalmente diferente, como indiquei em minha própria resposta.
— awendt
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.