Warum funktioniert diese HTTP-Anforderung unter AWS Lambda nicht?

87

Ich beginne mit AWS Lambda und versuche, einen externen Service von meiner Handlerfunktion anzufordern. Nach dieser Antwort sollten HTTP-Anforderungen einwandfrei funktionieren, und ich habe keine Dokumentation gefunden, die etwas anderes besagt. (Tatsächlich haben Benutzer Code veröffentlicht, der die Twilio-API zum Senden von SMS verwendet .)

Mein Handler-Code lautet:

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

und ich sehe die folgenden 4 Zeilen in meinen CloudWatch-Protokollen:

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

Ich würde dort eine weitere Zeile erwarten:

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

aber das fehlt. Wenn ich den wesentlichen Teil ohne den Handler-Wrapper im Knoten auf meinem lokalen Computer verwende, funktioniert der Code wie erwartet.

Das, was inputfile.txtich benutze, ist für den invoke-asyncAnruf:

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

Es scheint, dass der Teil des Handler-Codes, der die Anforderung ausführt, vollständig übersprungen wird. Ich begann mit der Anfrage lib und benutzte einfach http, um ein minimales Beispiel zu erstellen. Ich habe auch versucht, eine URL eines von mir kontrollierten Dienstes anzufordern, um die Protokolle zu überprüfen, und es gehen keine Anfragen ein.

Ich bin total ratlos. Gibt es einen Grund, warum Node und / oder AWS Lambda die HTTP-Anforderung nicht ausführen würden?

awendt
quelle
Ich denke, dass dies möglicherweise auf einen fehlenden Benutzeragenten in Ihrer HTTP-Anfrage zurückzuführen ist.
Ma'moon Al-Akash
4
Zum Zeitpunkt des Schreibens ist dies derzeit die Hauptfrage im Lambda-Forum der AWS-Foren. Es macht mich verrückt und auch ein paar andere Leute.
Nostradamus
@Nostradamus Ich freue mich über zusätzliche Rückmeldungen, Korrekturen und Upvotes. Schicken Sie sie hierher ;-)
awendt
1
Ich habe alles ausprobiert, vom Twillo-Beispiel bis zu einigen Standardbeispielen, die mit dem Alexa Node-Beispielpaket und Ihrer context.done () -Methode geliefert wurden. http POST funktioniert nicht. Ist es möglich, ein vollständiges Muster Ihres POST-Anforderungscodes zu veröffentlichen?
Chheplo

Antworten:

79

Natürlich habe ich das Problem falsch verstanden. Wie AWS selbst sagte :

Bei denjenigen, die zum ersten Mal in Lambda auf nodejs stoßen, wird häufig vergessen, dass Rückrufe asynchron ausgeführt werden und context.done()der ursprüngliche Handler aufgerufen wird, wenn Sie wirklich auf den Abschluss eines weiteren Rückrufs (z. B. einer S3.PUT-Operation) warten wollten, wodurch die Funktion erzwungen wird mit seiner Arbeit unvollständig zu beenden.

Ich habe lange context.donevor einem Rückruf für die angeforderte Anfrage angerufen und meine Funktion vorzeitig beendet.

Der Arbeitscode lautet:

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: Ab 2017 hat AWS die alten Nodejs 0.10 veraltet und nur die neuere 4.3-Laufzeit ist jetzt verfügbar (alte Funktionen sollten aktualisiert werden). Diese Laufzeit führte einige Änderungen an der Handlerfunktion ein. Der neue Handler hat jetzt 3 Parameter.

function(event, context, callback)

Obwohl Sie immer noch das finden wird succeed, doneund failauf dem Kontextparameter, schlagen AWS die verwendet werden callbackFunktion statt oder nullwird standardmäßig zurückgegeben.

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

Die vollständige Dokumentation finden Sie unter http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html

awendt
quelle
4
Wie funktioniert Ihr Handler-Code? Ich verstehe, dass Sie context.done () entfernen müssen, damit die Rückruffunktion aufgerufen wird. aber dein Code funktioniert immer noch nicht für mich. :(
mabeiyi
3
Der context.done()Anruf muss in die Rückrufe verschoben werden (für Erfolg und Fehler).
Awendt
2
Ich hatte Ihr Problem noch nicht, aber es ist großartig, daran zu denken, wenn ich mit Lambda weitermache.
David
Irgendwelche Ideen, wie ich eine API von Lambda in meinem lokalen System aufrufen kann?
Amit Kumar Ghosh
2
Requisiten zum Aktualisieren einer Frage 2015 mit Updates 2017!
Ass
16

Einfaches Arbeitsbeispiel für eine HTTP-Anforderung unter Verwendung eines Knotens.

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();
    });
}
smsivaprakaash
quelle
Danke dafür. Dies ist die beste Antwort, die ich 2019 auf dieser Seite sehe, nachdem Lambda die Warten-Syntax verwendet.
Taneem Tee
3
Ich habe über eine Stunde gebraucht, um die beste Antwort zu finden, da die Bibliotheken node-fetch requestusw. auf Lambda standardmäßig nicht verfügbar sind.
Alex C
Ein Großteil des Beispielcodes scheint jetzt kaputt zu sein. Dies ist ein funktionierender Beispielcode ab März 2020, der AWS Lambda mit Node.js 12.x verwendet
Muhammad Yussuf
Kann jemand erklären, wie POST-Anfragen mit Daten innerhalb von Lambda-Funktionen gestellt werden?
Pavindu
11

Ja, eine Antwort ist perfekt. Ich zeige nur meinen Arbeitscode ... Ich hatte den Kontext. Erfolgreich ('Blah'); Zeile direkt nach dem reqPost.end (); Linie. Das Verschieben an die Stelle, an der ich unten zeige, hat alles gelöst.

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

Ich habe dieses Problem in der Node 10.X-Version festgestellt. Unten ist mein Arbeitscode.

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();
};
Ameya Salagre
quelle
3

Ich hatte das gleiche Problem und dann wurde mir klar, dass sich die Programmierung in NodeJS tatsächlich von Python oder Java unterscheidet, da sie auf JavaScript basiert. Ich werde versuchen, einfache Konzepte zu verwenden, da es möglicherweise einige neue Leute gibt, die interessiert wären oder zu dieser Frage kommen könnten.

Schauen wir uns den folgenden Code an:

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

Wenn Sie eine Methode in http package (1) aufrufen, wird sie als Ereignis erstellt und dieses Ereignis erhält ein separates Ereignis. Die 'get'-Funktion (2) ist eigentlich der Ausgangspunkt dieses separaten Ereignisses.

Jetzt wird die Funktion bei (3) in einem separaten Ereignis ausgeführt, und Ihr Code setzt den Ausführungspfad fort und springt direkt zu (4) und beendet ihn, da nichts mehr zu tun ist.

Aber das bei (2) abgefeuerte Ereignis wird immer noch irgendwo ausgeführt und es wird seine eigene süße Zeit brauchen, um zu beenden. Ziemlich bizarr, oder? Nein, ist es nicht. So funktioniert NodeJS und es ist ziemlich wichtig, dass Sie sich mit diesem Konzept beschäftigen. Dies ist der Ort, an dem JavaScript-Versprechen helfen.

Weitere Informationen zu JavaScript-Versprechen finden Sie hier . Kurz gesagt, Sie benötigen ein JavaScript-Versprechen, um die Ausführung von Code inline zu halten, und es werden keine neuen / zusätzlichen Threads erzeugt.

Die meisten gängigen NodeJS-Pakete verfügen über eine versprochene Version ihrer API, es gibt jedoch auch andere Ansätze wie BlueBirdJS, mit denen das ähnliche Problem behoben werden kann.

Der Code, den Sie oben geschrieben haben, kann wie folgt lose neu geschrieben werden.

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

Bitte beachten Sie, dass der obige Code nicht direkt funktioniert, wenn Sie ihn in AWS Lambda importieren. Für Lambda müssen Sie die Module auch mit der Codebasis verpacken.

mmansoor
quelle
Ja, verspricht! Ich würde jedoch in Betracht ziehen, den context.done()Aufruf in eine verkettete finallyMethode zu verschieben.
Crftr
3

Ich habe im Internet viele Beiträge zu den verschiedenen Möglichkeiten gefunden, die Anfrage zu bearbeiten, aber keine, die tatsächlich zeigen, wie die Antwort auf AWS Lambda synchron verarbeitet wird.

Hier ist eine Lambda-Funktion des Knotens 6.10.3, die eine https-Anforderung verwendet, den gesamten Text der Antwort sammelt und zurückgibt und die Steuerung processBodymit den Ergebnissen an eine nicht aufgeführte Funktion übergibt . Ich glaube, dass http und https in diesem Code austauschbar sind.

Ich verwende das asynchrone Dienstprogrammmodul , das für Neulinge leichter zu verstehen ist. Sie müssen dies auf Ihren AWS Stack übertragen, um es verwenden zu können (ich empfehle das serverlose Framework ).

Beachten Sie, dass die Daten in Blöcken zurückkommen, die in einer globalen Variablen gesammelt sind, und schließlich der Rückruf aufgerufen wird, wenn die Daten endbearbeitet wurden.

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

Ja, es gibt tatsächlich viele Gründe, warum Sie auf AWS Lambda like und HTTP Endpoint zugreifen können.

Die Architektur von AWS Lambda

Es ist ein Microservice. Läuft in EC2 mit Amazon Linux AMI (Version 3.14.26–24.46.amzn1.x86_64) und läuft mit Node.js. Der Speicher kann zwischen 128 MB und 1 GB liegen. Wenn die Datenquelle das Ereignis auslöst, werden die Details als Parameter an eine Lambda-Funktion übergeben.

Was ist los?

AWS Lambda wird in einem Container ausgeführt, und der Code wird mit Paketen oder Modulen direkt in diesen Container hochgeladen. Zum Beispiel können wir NIEMALS SSH für den Linux-Rechner ausführen, auf dem Ihre Lambda-Funktion ausgeführt wird. Die einzigen Dinge, die wir überwachen können, sind die Protokolle mit CloudWatchLogs und der Ausnahme, die von der Laufzeit kam.

AWS kümmert sich für uns um das Starten und Beenden der Container und führt einfach den Code aus. Selbst wenn Sie require ('http') verwenden, wird es nicht funktionieren, da der Ort, an dem dieser Code ausgeführt wird, nicht dafür gemacht wurde.

Jonathanbaraldi
quelle
5
Sie haben mein Problem möglicherweise falsch verstanden. Ich weiß, dass Lambda-Code in einem Container ausgeführt wird, und ich weiß, dass ich nicht auf den zugrunde liegenden Computer zugreifen kann. Ich versuche auch nicht einzusteigen, mein Code versucht herauszukommen, dh auf externe Endpunkte zuzugreifen, und Lambda kann das ganz gut. Das Problem war etwas ganz anderes, wie ich in meiner eigenen Antwort hervorgehoben habe.
Awendt