เหตุใดคำขอ HTTP นี้จึงไม่ทำงานบน AWS Lambda


90

ฉันกำลังเริ่มต้นกับ AWS Lambda และฉันกำลังพยายามขอบริการภายนอกจากฟังก์ชันตัวจัดการของฉัน ตามคำตอบนี้คำขอ HTTP ควรใช้งานได้ดีและฉันไม่พบเอกสารใด ๆ ที่ระบุว่าเป็นอย่างอื่น (อันที่จริงมีคนโพสต์โค้ดที่ใช้ Twilio API เพื่อส่ง SMS )

รหัสตัวจัดการของฉันคือ:

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

และฉันเห็น 4 บรรทัดต่อไปนี้ในบันทึก 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

ฉันคาดหวังอีกบรรทัดในนั้น:

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

แต่ที่หายไป หากฉันใช้ส่วนที่สำคัญโดยไม่มีตัวจัดการตัวห่อในโหนดบนเครื่องโลคัลของฉันโค้ดจะทำงานตามที่คาดไว้

inputfile.txtฉันใช้มีไว้สำหรับinvoke-asyncโทรคือ:

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

ดูเหมือนว่าส่วนหนึ่งของโค้ดตัวจัดการที่ร้องขอจะถูกข้ามไปทั้งหมด ฉันเริ่มต้นด้วยการร้องขอ libและกลับไปใช้ธรรมดาhttpเพื่อสร้างตัวอย่างที่เรียบง่าย ฉันได้พยายามขอ URL ของบริการที่ฉันควบคุมเพื่อตรวจสอบบันทึกและไม่มีคำขอใด ๆ เข้ามา

ฉันนิ่งงันไปหมด มีเหตุผลใดบ้างที่ Node และ / หรือ AWS Lambda ไม่ดำเนินการตามคำขอ HTTP


ฉันคิดว่าสิ่งนี้อาจเกิดจาก user-agent หายไปในคำขอ HTTP ของคุณ
Ma'moon Al-Akash

4
ในขณะที่เขียนคำถามนี้เป็นคำถามยอดนิยมในฟอรัม Lambda ของฟอรัม AWS มันทำให้ฉันบ้าและยังมีคนอื่น ๆ อีกมากมายด้วย
Nostradamus

@Nostradamus ฉันขอขอบคุณข้อเสนอแนะการแก้ไขและการโหวตเพิ่มเติมใด ๆ ส่งมาที่นี่ ;-)
awendt

1
ฉันลองทุกอย่างตั้งแต่ตัวอย่าง Twillo ไปจนถึงตัวอย่างเริ่มต้นไม่กี่ตัวอย่างที่มาพร้อมกับกลุ่มตัวอย่างโหนด Alexa และวิธีการ context.done () http POST ไม่ทำงาน เป็นไปได้ไหมที่จะโพสต์ตัวอย่างรหัสคำขอ POST ทั้งหมดของคุณ
chheplo

คำตอบ:


80

แน่นอนฉันเข้าใจผิดว่าปัญหานี้ ตามที่ AWS วางไว้ :

สำหรับผู้ที่พบ nodejs เป็นครั้งแรกใน Lambda ข้อผิดพลาดทั่วไปคือลืมว่าการเรียกกลับดำเนินการแบบอะซิงโครนัสและเรียก context.done()ในตัวจัดการเดิมเมื่อคุณต้องการรอการโทรกลับอื่น (เช่นการดำเนินการ S3.PUT) ให้เสร็จสมบูรณ์บังคับให้ฟังก์ชัน ที่จะยุติโดยที่งานไม่สมบูรณ์

ฉันกำลังโทรหาcontext.doneทางก่อนที่จะมีการเรียกกลับสำหรับคำขอเริ่มทำงานทำให้ฟังก์ชันของฉันสิ้นสุดลงก่อนเวลา

รหัสการทำงานคือ:

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

อัปเดต:ตั้งแต่ปี 2017 AWS ได้เลิกใช้งาน Nodejs 0.10 เก่าและขณะนี้มีเฉพาะรันไทม์ 4.3 ที่ใหม่กว่าเท่านั้น (ควรอัปเดตฟังก์ชันเก่า) รันไทม์นี้แนะนำการเปลี่ยนแปลงบางอย่างกับฟังก์ชันตัวจัดการ ตอนนี้ตัวจัดการใหม่มี 3 พารามิเตอร์

function(event, context, callback)

แม้ว่าคุณจะยังคงพบว่าsucceed, doneและfailพารามิเตอร์บริบท AWS ขอแนะนำให้ใช้callbackฟังก์ชั่นแทนหรือnullจะถูกส่งกลับไปโดยปริยาย

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

สามารถดูเอกสารฉบับสมบูรณ์ได้ที่http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html


4
แล้วคุณจะทำให้โค้ดตัวจัดการของคุณทำงานได้อย่างไร? ฉันเข้าใจว่าคุณต้องลบ context.done () เพื่อที่จะเรียกใช้ฟังก์ชันการโทรกลับ แต่รหัสของคุณยังใช้ไม่ได้สำหรับฉัน :(
mabeiyi

3
context.done()โทรจะต้องมีการย้ายเข้ามาเรียกกลับ (สำหรับความสำเร็จและกรณีข้อผิดพลาด)
awendt

2
ยังไม่มีปัญหาของคุณ แต่เป็นสิ่งที่ดีที่ควรคำนึงถึงเมื่อฉันก้าวไปข้างหน้ากับแลมด้า
David

ความคิดใด ๆ เกี่ยวกับวิธีเรียกใช้ api ในระบบโลคัลจาก Lambda
Amit Kumar Ghosh

2
อุปกรณ์ประกอบการอัปเดตคำถามปี 2558 พร้อมอัปเดตปี 2560!
Ace

21

ตัวอย่างการทำงานอย่างง่ายของคำขอ Http โดยใช้โหนด

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

ขอบคุณสำหรับสิ่งนี้. นี่คือคำตอบที่ดีที่สุดที่ฉันเห็นในหน้านี้ในปี 2019 ตอนนี้ Lambda กำลังใช้ไวยากรณ์ await
Taneem Tee

3
ฉันใช้เวลามากกว่าหนึ่งชั่วโมงในการค้นหาและคำตอบที่ดีที่สุดเนื่องจาก libs และnode-fetch requestอื่น ๆ ไม่สามารถใช้ได้ใน Lambda โดยค่าเริ่มต้น
Alex C

โค้ดตัวอย่างจำนวนมากดูเหมือนจะเสียแล้ว นี่คือโค้ดตัวอย่างที่ใช้งานได้ในเดือนมีนาคม 2020 โดยใช้ AWS Lambda กับ Node.js 12.x
Muhammad Yussuf

ใครช่วยอธิบายวิธีสร้างคำขอ POST ด้วยข้อมูลภายในฟังก์ชันแลมบ์ดาได้ไหม
Pavindu

11

ใช่คำตอบที่ยอดเยี่ยมสมบูรณ์แบบ ฉันจะแสดงรหัสการทำงานของฉัน ... ฉันมีcontext.succeed ('Blah'); บรรทัดหลังreqPost.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();
};

4

ฉันประสบปัญหานี้ในเวอร์ชันโหนด 10.X ด้านล่างนี้คือรหัสการทำงานของฉัน

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

ฉันมีปัญหาเดียวกันและจากนั้นฉันก็รู้ว่าการเขียนโปรแกรมใน NodeJS นั้นแตกต่างจาก Python หรือ Java เนื่องจากใช้ JavaScript ฉันจะพยายามใช้แนวคิดง่ายๆเพราะอาจมีคนใหม่ ๆ บางคนที่สนใจหรืออาจจะเจอคำถามนี้

ลองดูรหัสต่อไปนี้:

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

เมื่อใดก็ตามที่คุณโทรไปยังเมธอดในแพ็กเกจ http (1) ระบบจะสร้างเป็นเหตุการณ์และเหตุการณ์นี้ทำให้มันแยกเหตุการณ์ ฟังก์ชัน 'get' (2) เป็นจุดเริ่มต้นของเหตุการณ์ที่แยกจากกันนี้

ตอนนี้ฟังก์ชั่นที่ (3) จะทำงานในเหตุการณ์ที่แยกจากกันและโค้ดของคุณจะดำเนินการตามเส้นทางต่อไปและจะข้ามไปที่ (4) และจบการทำงานเนื่องจากไม่มีอะไรต้องทำอีกต่อไป

แต่เหตุการณ์ที่ยิงที่ (2) ยังคงดำเนินอยู่ที่ไหนสักแห่งและจะต้องใช้เวลาอันแสนหวานในการดำเนินการให้เสร็จสิ้น แปลกมากใช่มั้ย. ดีไม่มันไม่ได้ นี่คือวิธีการทำงานของ NodeJS และเป็นสิ่งสำคัญมากที่คุณจะต้องนึกถึงแนวคิดนี้ นี่คือสถานที่ที่ JavaScript Promises เข้ามาช่วย

คุณสามารถอ่านเพิ่มเติมเกี่ยวกับสัญญา JavaScript ที่นี่ โดยสรุปคุณจะต้องมี JavaScript Promise เพื่อให้การทำงานของโค้ดอยู่ในบรรทัดและจะไม่เกิดเธรดใหม่ / เพิ่มเติม

แพ็คเกจ NodeJS ทั่วไปส่วนใหญ่มี API เวอร์ชันที่สัญญาไว้ให้ใช้งานได้ แต่มีวิธีการอื่น ๆ เช่น BlueBirdJS ที่แก้ไขปัญหาที่คล้ายกัน

โค้ดที่คุณเขียนไว้ข้างต้นสามารถเขียนซ้ำได้ดังนี้

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

โปรดทราบว่าโค้ดด้านบนจะใช้ไม่ได้โดยตรงหากคุณจะนำเข้าใน AWS Lambda สำหรับ Lambda คุณจะต้องแพคเกจโมดูลด้วยฐานรหัสด้วย


ใช่สัญญา! แม้ว่าฉันจะพิจารณาย้ายการcontext.done()โทรเป็นfinallyวิธีการผูกมัด
crftr

3

ฉันพบโพสต์มากมายบนเว็บเกี่ยวกับวิธีต่างๆในการทำคำขอ แต่ไม่มีเลยที่แสดงวิธีประมวลผลการตอบกลับพร้อมกันบน AWS Lambda

นี่คือฟังก์ชันแลมด้าโหนด 6.10.3 ที่ใช้คำขอ https รวบรวมและส่งคืนเนื้อหาทั้งหมดของการตอบสนองและส่งผ่านการควบคุมไปยังฟังก์ชันที่ไม่อยู่ในรายการprocessBodyพร้อมกับผลลัพธ์ ฉันเชื่อว่า http และ https ใช้แทนกันได้ในรหัสนี้

ฉันใช้โมดูลยูทิลิตี้ asyncซึ่งง่ายต่อการเข้าใจสำหรับมือใหม่ คุณจะต้องส่งข้อมูลนั้นไปยัง AWS Stack เพื่อใช้งาน (ขอแนะนำให้ใช้เฟรมเวิร์กแบบไร้เซิร์ฟเวอร์ )

โปรดทราบว่าข้อมูลจะกลับมาเป็นชิ้น ๆ ซึ่งรวบรวมในตัวแปรส่วนกลางและในที่สุดการเรียกกลับจะถูกเรียกเมื่อข้อมูลมีการ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);
        });
    }
};


-14

ใช่มีสาเหตุหลายประการที่ทำให้คุณเข้าถึง AWS Lambda like และ HTTP Endpoint ได้

สถาปัตยกรรมของ AWS Lambda

มันเป็นบริการไมโคร ทำงานภายใน EC2 ด้วย Amazon Linux AMI (เวอร์ชัน 3.14.26–24.46.amzn1.x86_64) และรันด้วย Node.js หน่วยความจำสามารถเป็นขนาด 128mb และ 1gb เมื่อแหล่งข้อมูลทริกเกอร์เหตุการณ์รายละเอียดจะถูกส่งผ่านไปยังฟังก์ชัน Lambda เป็นพารามิเตอร์

เกิดอะไรขึ้น?

AWS Lambda run อยู่ภายในคอนเทนเนอร์และโค้ดจะถูกอัปโหลดไปยังคอนเทนเนอร์นี้โดยตรงพร้อมแพ็คเกจหรือโมดูล ตัวอย่างเช่นเราไม่สามารถทำ SSH สำหรับเครื่อง linux ที่เรียกใช้ฟังก์ชัน lambda ของคุณได้ สิ่งเดียวที่เราตรวจสอบได้คือบันทึกด้วย CloudWatchLogs และข้อยกเว้นที่มาจากรันไทม์

AWS ดูแลการเปิดตัวและยุติคอนเทนเนอร์ให้เราและเพียงแค่เรียกใช้โค้ด ดังนั้นแม้ว่าคุณจะใช้ need ('http') มันก็จะไม่ทำงานเพราะที่ที่โค้ดนี้ทำงานไม่ได้ถูกสร้างขึ้นมาเพื่อสิ่งนี้


5
คุณอาจเข้าใจปัญหาของฉันผิด ฉันทราบเกี่ยวกับโค้ดแลมบ์ดาที่ทำงานในคอนเทนเนอร์และฉันรู้ว่าฉันไม่สามารถเข้าถึงเครื่องที่อยู่เบื้องหลัง ฉันไม่ได้พยายามเข้ามา แต่รหัสของฉันพยายามที่จะออกเช่นเข้าถึงจุดสิ้นสุดภายนอกและแลมด้าก็ทำได้ดีทีเดียว ปัญหาเป็นอย่างอื่นโดยสิ้นเชิงดังที่ฉันได้ชี้ให้เห็นในคำตอบของฉันเอง
awendt
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.