Perché questa richiesta HTTP non funziona su AWS Lambda?


89

Sto iniziando con AWS Lambda e sto cercando di richiedere un servizio esterno dalla mia funzione gestore. Secondo questa risposta , le richieste HTTP dovrebbero funzionare bene e non ho trovato alcuna documentazione che dica il contrario. (In effetti, le persone hanno pubblicato codice che utilizza l'API Twilio per inviare SMS .)

Il mio codice gestore è:

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 vedo le seguenti 4 righe nei miei log di 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

Mi aspetto un'altra riga lì dentro:

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

ma questo manca. Se sto usando la parte essenziale senza il wrapper del gestore nel nodo sulla mia macchina locale, il codice funziona come previsto.

Quello inputfile.txtche sto usando è per la invoke-asyncchiamata è questo:

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

Sembra che la parte del codice del gestore che esegue la richiesta venga saltata completamente. Ho iniziato con la richiesta lib e sono tornato a usare plain httpper creare un esempio minimo. Ho anche provato a richiedere un URL di un servizio che controllo per controllare i log e non ci sono richieste in arrivo.

Sono totalmente perplesso. C'è qualche motivo per cui Node e / o AWS Lambda non eseguono la richiesta HTTP?


Penso che ciò potrebbe essere causato da un user-agent mancante nella tua richiesta HTTP.
Ma'moon Al-Akash,

4
Al momento in cui scrivo, questa è attualmente la domanda principale nel forum Lambda dei forum AWS. Mi sta facendo impazzire e anche un sacco di altre persone.
Nostradamus

@Nostradamus Apprezzo eventuali commenti, correzioni e voti positivi aggiuntivi.
Mandali

1
Ho provato di tutto, dall'esempio Twillo ad alcuni esempi predefiniti forniti con il bundle di esempio del nodo Alexa e anche il tuo metodo context.done (). http POST non funziona. È possibile inviare un campione completo del codice di richiesta POST?
chheplo

Risposte:


79

Ovviamente stavo fraintendendo il problema. Come affermano gli stessi AWS :

Per coloro che incontrano nodejs per la prima volta in Lambda, un errore comune è dimenticare che i callback vengono eseguiti in modo asincrono e chiamare context.done()il gestore originale quando si intendeva attendere il completamento di un altro callback (come un'operazione S3.PUT), forzando la funzione terminare con il suo lavoro incompleto.

Stavo chiamando context.donemolto prima che venissero attivati ​​i callback per la richiesta, provocando la chiusura anticipata della mia funzione.

Il codice di lavoro è questo:

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

Aggiornamento: a partire dal 2017 AWS ha deprecato il vecchio Nodejs 0.10 ed è ora disponibile solo il run-time 4.3 più recente (le vecchie funzioni dovrebbero essere aggiornate). Questo runtime ha introdotto alcune modifiche alla funzione handler. Il nuovo gestore ha ora 3 parametri.

function(event, context, callback)

Anche se si continua a trovare il succeed, donee failil parametro di contesto, AWS suggeriscono di utilizzare la callbackfunzione invece o nullviene restituito per impostazione predefinita.

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

La documentazione completa può essere trovata su http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html


4
quindi, come si fa a far funzionare il codice del gestore? a quanto mi risulta è necessario rimuovere context.done () in modo che venga chiamata la funzione di callback. ma il tuo codice continua a non funzionare per me. :(
mabeiyi

3
La context.done()chiamata deve essere spostata nei callback (in caso di successo e di errore).
awendt

2
non ho ancora avuto il tuo problema, ma è fantastico tenerlo a mente mentre vado avanti con lambda.
David

qualche idea su come posso invocare un'API nel mio sistema locale da Lambda?
Amit Kumar Ghosh

2
puntelli per aggiornare una domanda del 2015 con gli aggiornamenti del 2017!
Ace

19

Semplice esempio di lavoro di richiesta HTTP utilizzando 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();
    });
}

Grazie per questo. Questa è la migliore risposta che vedo in questa pagina nel 2019, ora che Lambda utilizza la sintassi di attesa.
Taneem Tee

3
Mi ci è voluta più di un'ora per trovare una risposta migliore poiché le librerie node-fetch requestecc. Non sono disponibili su Lambda per impostazione predefinita.
Alex C

Molto del codice di esempio là fuori sembra essere rotto ora. Questo codice di esempio funziona a partire da marzo 2020, utilizzando AWS Lambda con Node.js 12.x
Muhammad Yussuf

Qualcuno può spiegare come effettuare richieste POST con dati all'interno delle funzioni lambda?
Pavindu

11

Sì, la risposta è perfetta. Mostrerò solo il mio codice funzionante ... Ho avuto il context.succeed ('Blah'); riga subito dopo reqPost.end (); linea. Spostandolo dove mostro sotto ha risolto tutto.

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

Ho riscontrato questo problema nella versione del nodo 10.X. di seguito è il mio codice di lavoro.

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

Ho avuto lo stesso problema e poi mi sono reso conto che la programmazione in NodeJS è in realtà diversa da Python o Java in quanto basata su JavaScript. Cercherò di utilizzare concetti semplici in quanto potrebbero esserci alcune nuove persone che sarebbero interessate o potrebbero venire a questa domanda.

Diamo un'occhiata al seguente codice:

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

Ogni volta che si effettua una chiamata a un metodo nel pacchetto http (1), viene creato come evento e questo evento lo riceve come evento separato. La funzione "get" (2) è in realtà il punto di partenza di questo evento separato.

Ora, la funzione in (3) verrà eseguita in un evento separato e il tuo codice continuerà con il percorso di esecuzione e salterà direttamente a (4) e lo finirà, perché non c'è più niente da fare.

Ma l'evento sparato a (2) è ancora in esecuzione da qualche parte e ci vorrà il suo tempo per finire. Abbastanza bizzarro, vero? Beh, no, non lo è. Questo è il modo in cui funziona NodeJS ed è piuttosto importante che tu ti avvolga intorno a questo concetto. Questo è il luogo in cui le promesse JavaScript vengono in aiuto.

Puoi leggere di più sulle promesse JavaScript qui . In poche parole, avresti bisogno di una promessa JavaScript per mantenere l'esecuzione del codice in linea e non genererà thread nuovi / extra.

La maggior parte dei pacchetti NodeJS comuni ha una versione promessa della loro API disponibile, ma ci sono altri approcci come BlueBirdJS che risolvono il problema simile.

Il codice che avevi scritto sopra può essere riscritto liberamente come 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);
};

Tieni presente che il codice precedente non funzionerà direttamente se lo importi in AWS Lambda. Per Lambda, dovrai anche impacchettare i moduli con la base di codice.


Sì, promesse! Sebbene prenderei in considerazione l' context.done()idea di spostare la chiamata in un finallymetodo concatenato .
crftr

3

Ho trovato molti post sul Web sui vari modi per eseguire la richiesta, ma nessuno che mostra effettivamente come elaborare la risposta in modo sincrono su AWS Lambda.

Ecco una funzione lambda del nodo 6.10.3 che utilizza una richiesta https, raccoglie e restituisce il corpo completo della risposta e passa il controllo a una funzione non elencata processBodycon i risultati. Credo che http e https siano intercambiabili in questo codice.

Sto usando il modulo di utilità asincrono , che è più facile da capire per i neofiti. Dovrai inviarlo al tuo stack AWS per usarlo (consiglio il framework serverless ).

Si noti che i dati tornano in blocchi, che sono raccolti in una variabile globale, e infine il callback viene chiamato quando i dati sono stati modificati 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);
        });
    }
};

0

inserisci qui la descrizione dell'immagine

Aggiungi il codice sopra nel gateway API nella sezione GET-Integration Request> mapping.


-14

Sì, in effetti ci sono molte ragioni per cui puoi accedere ad AWS Lambda come e all'endpoint HTTP.

L'architettura di AWS Lambda

È un microservizio. Funziona all'interno di EC2 con Amazon Linux AMI (versione 3.14.26–24.46.amzn1.x86_64) e funziona con Node.js. La memoria può essere compresa tra 128 MB e 1 GB. Quando l'origine dati attiva l'evento, i dettagli vengono passati a una funzione Lambda come parametri.

Cosa succede?

AWS Lambda viene eseguito all'interno di un contenitore e il codice viene caricato direttamente in questo contenitore con pacchetti o moduli. Ad esempio, non possiamo MAI eseguire SSH per la macchina Linux che esegue la funzione lambda. Le uniche cose che possiamo monitorare sono i log, con CloudWatchLogs e l'eccezione che proviene dal runtime.

AWS si occupa di avviare e terminare i container per noi e di eseguire semplicemente il codice. Quindi, anche se usi require ('http'), non funzionerà, perché il luogo in cui viene eseguito questo codice non è stato creato per questo.


5
Potresti aver frainteso il mio problema. So che il codice Lambda viene eseguito in un contenitore e so di non poter accedere alla macchina sottostante. Né sto cercando di entrare, il mio codice sta cercando di uscire, cioè accedere a endpoint esterni, e Lambda può farlo abbastanza bene. Il problema era completamente diverso, come ho sottolineato nella mia risposta.
awendt
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.