Tại sao yêu cầu HTTP này không hoạt động trên AWS Lambda?


89

Tôi đang bắt đầu với AWS Lambda và tôi đang cố gắng yêu cầu một dịch vụ bên ngoài từ hàm xử lý của mình. Theo câu trả lời này , các yêu cầu HTTP sẽ hoạt động tốt và tôi không tìm thấy bất kỳ tài liệu nào nói khác. (Trên thực tế, mọi người đã đăng mã sử dụng API Twilio để gửi SMS .)

Mã trình xử lý của tôi là:

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

và tôi thấy 4 dòng sau trong nhật ký CloudWatch của mình:

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

Tôi mong đợi một dòng khác trong đó:

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

nhưng đó là thiếu. Nếu tôi đang sử dụng phần thiết yếu mà không có trình bao bọc trình xử lý trong nút trên máy cục bộ của mình, thì mã sẽ hoạt động như mong đợi.

Các inputfile.txttôi đang sử dụng là dành cho các invoke-asynccuộc gọi là thế này:

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

Có vẻ như phần mã xử lý thực hiện yêu cầu bị bỏ qua hoàn toàn. Tôi bắt đầu với lib yêu cầu và quay lại sử dụnghttp để tạo một ví dụ tối thiểu. Tôi cũng đã cố gắng yêu cầu URL của dịch vụ mà tôi kiểm soát để kiểm tra nhật ký và không có yêu cầu nào đến.

Tôi hoàn toàn bối rối. Có lý do gì khiến Node và / hoặc AWS Lambda không thực thi yêu cầu HTTP không?


Tôi nghĩ rằng điều này có thể do thiếu tác nhân người dùng trong yêu cầu HTTP của bạn.
Ma'moon Al-Akash,

4
Tại thời điểm viết bài, đây hiện là câu hỏi hàng đầu trong diễn đàn Lambda của các diễn đàn AWS. Nó khiến tôi phát điên và nhiều người khác nữa.
Nostradamus

@Nostradamus Tôi đánh giá cao bất kỳ phản hồi bổ sung, chỉnh sửa và ủng hộ nào. Gửi chúng ở đây ;-)
awendt

1
Tôi đã thử mọi thứ từ ví dụ Twillo đến một vài ví dụ mặc định được vận chuyển với gói ví dụ nút Alexa và cả phương thức context.done () của bạn. http POST không hoạt động. Có thể đăng mẫu hoàn chỉnh của mã yêu cầu POST của bạn không?
chheplo

Câu trả lời:


79

Tất nhiên, tôi đã hiểu sai vấn đề. Như chính AWS đã nói :

Đối với những người gặp phải nodejs lần đầu tiên trong Lambda, một lỗi phổ biến là quên rằng các lệnh gọi lại thực thi không đồng bộ và gọi context.done()trong trình xử lý ban đầu khi bạn thực sự muốn đợi một lệnh gọi lại khác (chẳng hạn như hoạt động S3.PUT) hoàn thành, buộc hàm chấm dứt với công việc của nó chưa hoàn thành.

Tôi đang gọi context.donetrước khi bất kỳ lệnh gọi lại nào cho yêu cầu được kích hoạt, khiến chức năng của tôi bị chấm dứt trước thời hạn.

Mã làm việc là:

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

Cập nhật: bắt đầu từ năm 2017 AWS đã ngừng sử dụng Nodejs 0.10 cũ và hiện chỉ có thời gian chạy 4.3 mới hơn (nên cập nhật các chức năng cũ). Thời gian chạy này đã giới thiệu một số thay đổi đối với chức năng xử lý. Trình xử lý mới hiện có 3 tham số.

function(event, context, callback)

Mặc dù bạn vẫn sẽ tìm ra succeed, donefailtrên các tham số ngữ cảnh, AWS gợi ý để sử dụng callbackchức năng thay vì hay nullđược trả lại theo mặc định.

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

Toàn bộ tài liệu có thể được tìm thấy tại http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html


4
vậy, làm thế nào để bạn làm cho mã trình xử lý của bạn hoạt động? Tôi hiểu là bạn cần loại bỏ context.done () để hàm gọi lại sẽ được gọi. nhưng mã của bạn vẫn không hoạt động cho tôi. :(
mabeiyi

3
Cuộc context.done()gọi cần được chuyển vào các cuộc gọi lại (đối với trường hợp thành công và lỗi).
awendt

2
vẫn chưa có vấn đề của bạn, nhưng rất cần lưu ý khi tôi tiếp tục với lambda.
David

bất kỳ ý tưởng nào về cách tôi có thể gọi một api trong hệ thống cục bộ của mình từ Lambda?
Amit Kumar Ghosh

2
đạo cụ để cập nhật câu hỏi năm 2015 với bản cập nhật năm 2017!
Ace

19

Làm việc đơn giản Ví dụ về yêu cầu Http sử dụng nút.

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

Cảm ơn vì điều này. Đây là câu trả lời hay nhất mà tôi thấy trên trang này vào năm 2019, hiện tại Lambda đang sử dụng cú pháp await.
Taneem Tee

3
Điều này khiến tôi mất hơn một giờ để tìm và trả lời tốt nhất vì libs, node-fetch requestv.v. không có sẵn trên Lambda theo mặc định.
Alex C

Rất nhiều mã mẫu hiện có vẻ đã bị hỏng. Đây là mã mẫu hoạt động kể từ tháng 3 năm 2020, sử dụng AWS Lambda với Node.js 12.x
Muhammad Yussuf

Ai đó có thể giải thích cách thực hiện yêu cầu POST với dữ liệu bên trong các hàm lambda không?
Pavindu

11

Vâng, câu trả lời của awendt là hoàn hảo. Tôi sẽ chỉ hiển thị mã làm việc của mình ... Tôi đã có context.succeed ('Blah'); dòng ngay sau reqPost.end (); hàng. Di chuyển nó đến nơi tôi hiển thị bên dưới đã giải quyết mọi thứ.

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

Tôi đã gặp phải vấn đề này trên phiên bản Node 10.X. dưới đây là mã làm việc của tôi.

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

Tôi đã gặp vấn đề tương tự và sau đó tôi nhận ra rằng lập trình bằng NodeJS thực sự khác với Python hoặc Java vì nó dựa trên JavaScript. Tôi sẽ cố gắng sử dụng các khái niệm đơn giản vì có thể có một số người mới quan tâm hoặc có thể đặt câu hỏi này.

Hãy xem đoạn mã sau:

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

Bất cứ khi nào bạn thực hiện cuộc gọi đến một phương thức trong gói http (1), nó sẽ được tạo dưới dạng sự kiện và sự kiện này nhận nó là sự kiện riêng biệt. Hàm 'get' (2) thực sự là điểm bắt đầu của sự kiện riêng biệt này.

Bây giờ, hàm tại (3) sẽ được thực thi trong một sự kiện riêng biệt và mã của bạn sẽ tiếp tục đường dẫn thực thi của nó và sẽ chuyển thẳng đến (4) và kết thúc nó, vì không còn gì phải làm nữa.

Nhưng sự kiện được kích hoạt tại (2) vẫn đang diễn ra ở đâu đó và sẽ mất thời gian ngọt ngào để kết thúc. Khá kỳ lạ, phải không ?. Vâng, không, nó không phải là. Đây là cách NodeJS hoạt động và điều khá quan trọng là bạn phải hiểu khái niệm này. Đây là nơi mà JavaScript Promises sẽ giúp đỡ.

Bạn có thể đọc thêm về JavaScript Promises tại đây . Tóm lại, bạn sẽ cần một Lời hứa JavaScript để giữ cho việc thực thi mã nội tuyến và sẽ không tạo ra các chuỗi mới / bổ sung.

Hầu hết các gói NodeJS phổ biến đều có sẵn phiên bản Promised của API, nhưng có những cách tiếp cận khác như BlueBirdJS giải quyết vấn đề tương tự.

Đoạn mã bạn đã viết ở trên có thể được viết lại một cách lỏng lẻo như sau.

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

Xin lưu ý rằng mã trên sẽ không hoạt động trực tiếp nếu bạn nhập mã đó trong AWS Lambda. Đối với Lambda, bạn cũng sẽ cần phải đóng gói các mô-đun bằng cơ sở mã.


Vâng, những lời hứa! Mặc dù tôi sẽ cân nhắc việc chuyển context.done()cuộc gọi thành một finallyphương thức chuỗi .
crftr

3

Tôi đã tìm thấy rất nhiều bài đăng trên web về các cách khác nhau để thực hiện yêu cầu, nhưng không có bài nào thực sự chỉ ra cách xử lý phản hồi đồng bộ trên AWS Lambda.

Đây là một hàm lambda Node 6.10.3 sử dụng một yêu cầu https, thu thập và trả về toàn bộ phần nội dung của phản hồi, đồng thời chuyển quyền kiểm soát cho một hàm không công khai processBody với kết quả. Tôi tin rằng http và https có thể hoán đổi cho nhau trong mã này.

Tôi đang sử dụng mô-đun tiện ích không đồng bộ , dễ hiểu hơn cho người mới. Bạn sẽ cần đẩy nó vào AWS Stack của mình để sử dụng nó (tôi khuyên bạn nên sử dụng framework không máy chủ ).

Lưu ý rằng dữ liệu quay trở lại thành từng phần, được tập hợp trong một biến toàn cục và cuối cùng, lệnh gọi lại được gọi khi dữ liệu đã chỉnh sửa 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

nhập mô tả hình ảnh ở đây

Thêm mã trên vào cổng API trong phần GET-Integration Request> phần ánh xạ.


-14

Có, trên thực tế có nhiều lý do tại sao bạn có thể truy cập AWS Lambda like và HTTP Endpoint.

Kiến trúc của AWS Lambda

Đó là một dịch vụ vi mô. Chạy bên trong EC2 với Amazon Linux AMI (Phiên bản 3.14.26–24.46.amzn1.x86_64) và chạy với Node.js. Bộ nhớ có thể là 128mb và 1gb. Khi nguồn dữ liệu kích hoạt sự kiện, các chi tiết được chuyển đến một hàm Lambda dưới dạng tham số.

Sao vậy?

AWS Lambda chạy bên trong một vùng chứa và mã được tải trực tiếp lên vùng chứa này cùng với các gói hoặc mô-đun. Ví dụ, chúng tôi KHÔNG BAO GIỜ có thể thực hiện SSH cho máy linux chạy hàm lambda của bạn. Những thứ duy nhất mà chúng tôi có thể theo dõi là nhật ký, với CloudWatchLogs và ngoại lệ đến từ thời gian chạy.

AWS lo việc khởi chạy và kết thúc vùng chứa cho chúng tôi và chỉ cần chạy mã. Vì vậy, ngay cả khi bạn sử dụng request ('http'), nó sẽ không hoạt động, vì nơi mã này chạy, không được tạo ra cho việc này.


5
Bạn có thể đã hiểu sai vấn đề của tôi. Tôi biết về việc mã Lambda đang được chạy trong vùng chứa và tôi biết mình không thể truy cập vào máy bên dưới. Tôi cũng không cố vào được, mã của tôi đang cố thoát ra, tức là truy cập vào các điểm cuối bên ngoài và Lambda có thể làm điều đó khá tốt. Vấn đề là một cái gì đó hoàn toàn khác, như tôi đã chỉ ra trong câu trả lời của riêng mình.
awendt
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.