为什么此HTTP请求无法在AWS Lambda上运行?

awe*_*ndt 74 httprequest amazon-web-services node.js aws-lambda

我开始使用AWS Lambda,我正在尝试从我的处理程序函数请求外部服务.根据这个答案,HTTP请求应该可以正常工作,我没有找到任何其他说明的文档.(事实上​​,人们已经发布了使用Twilio API发送短信的代码.)

我的处理程序代码是:

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);
}
Run Code Online (Sandbox Code Playgroud)

我在CloudWatch日志中看到以下4行:

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
Run Code Online (Sandbox Code Playgroud)

我期待那里有另一条线:

2015-02-11 07:38:06 UTC eb19c89d-b1c0-11e4-bceb-d310b88d37e2 Got response: 302
Run Code Online (Sandbox Code Playgroud)

但那是缺失的.如果我在本地计算机上的节点中使用了没有处理程序包装器的基本部分,则代码按预期工作.

inputfile.txt我使用的是用于invoke-async调用是这样的:

{
   "url":"http://www.google.com"
}
Run Code Online (Sandbox Code Playgroud)

看起来处理程序代码中执行请求的部分完全被跳过.我从请求lib开始,然后回到使用plain http来创建一个最小的例子.我还试图请求我控制的服务的URL来检查日志,并且没有请求进入.

我完全难过了.是否有任何理由Node和/或AWS Lambda不会执行HTTP请求?

awe*_*ndt 74

当然,我误解了这个问题.正如AWS自己所说:

对于那些在Lambda中第一次遇到nodejs的人来说,一个常见的错误就是忘记了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);
}
Run Code Online (Sandbox Code Playgroud)

更新:从2017年开始AWS已弃用旧的Nodejs 0.10,现在只有较新的4.3运行时可用(旧功能应该更新).此运行时引入了对处理函数的一些更改.新的处理程序现在有3个参数.

function(event, context, callback)
Run Code Online (Sandbox Code Playgroud)

虽然你仍然会发现succeed,donefail上下文参数,AWS建议使用的callback功能,而不是或null默认情况下返回.

callback(new Error('failure')) // to return error
callback(null, 'success msg') // to return ok
Run Code Online (Sandbox Code Playgroud)

有关完整文档,请访问http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html

  • 那么,你如何使你的处理程序代码工作?我的理解是你需要删除context.done()以便调用回调函数.但是你的代码仍然不适合我.:( (4认同)
  • 需要将`context.done()`调用移入回调(成功和错误情况). (3认同)
  • 还没有你的问题,但是当我向前推进lambda时要记住这一点. (2认同)
  • 用2017年更新更新2015年问题的道具! (2认同)

imT*_*chu 11

是的,awendt答案是完美的.我只是展示我的工作代码......我有了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();
};
Run Code Online (Sandbox Code Playgroud)


Chr*_*yes 9

现代异步/等待示例

您需要防止 lambda 在 https 请求完成之前完成。它也使具有多个请求的代码更易于阅读。

const https = require('https');

// Helper that turns https.request into a promise
function httpsRequest(options) {
    return new Promise((resolve, reject) => {
        const req = https.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);
        });
        
        req.end();
    });
}

// Lambda starts executing here
exports.handler = async event => {
    // --- GET example request  
    var options = {
        method: 'GET',
        hostname: 'postman-echo.com',
        path: encodeURI('/get?foo1=bar1'),
        headers: {
        },
    };

    try {
        const getBody = await httpsRequest(options);
        // The console.log below will not run until the GET request above finishes
        console.log('GET completed successfully! Response body:', getBody);
    } catch (err) {
        console.error('GET request failed, error:', err);
    }

    // --- POST example request  
    var options = {
        method: 'POST',
        hostname: 'postman-echo.com',
        path: encodeURI('/hi/there?hand=wave'),
        headers: {
        },
    };

    try {
        const postBody = await httpsRequest(options);
        // The console.log below will not run until the POST request above finishes
        console.log('POST response body:', postBody);
    } catch (err) {
        console.error('POST request failed, error:', err);
    }
};
Run Code Online (Sandbox Code Playgroud)


Ame*_*gre 7

我在 Node 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();
};
Run Code Online (Sandbox Code Playgroud)


sms*_*ash 6

使用节点的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();
    });
}
Run Code Online (Sandbox Code Playgroud)

  • 我花了一个多小时才找到最佳答案,因为默认情况下 Lambda 上不提供库“node-fetch”“request”等。 (4认同)

mma*_*oor 5

我遇到了同样的问题,然后我意识到 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)
}
Run Code Online (Sandbox Code Playgroud)

每当您调用 http 包 (1) 中的方法时,它都会被创建为事件,并且该事件会获取单独的事件。“get”函数 (2) 实际上是这个单独事件的起点。

现在,(3) 处的函数将在一个单独的事件中执行,您的代码将继续执行它的路径,并直接跳转到 (4) 并结束它,因为没有更多的事情可做。

但在 (2) 处触发的事件仍在某处执行,并且需要一段美好的时间才能完成。很奇怪,对吧?嗯,不,不是。这就是 NodeJS 的工作原理,理解这个概念非常重要。这就是 JavaScript Promise 可以提供帮助的地方。

您可以在此处阅读有关 JavaScript Promise 的更多信息。简而言之,您需要一个 JavaScript Promise 来保持代码的内联执行,并且不会产生新的/额外的线程。

大多数常见的 NodeJS 包都有可用的 API 的 Promised 版本,但还有其他方法(例如 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);
};
Run Code Online (Sandbox Code Playgroud)

请注意,如果将上述代码导入到 AWS Lambda 中,则该代码将无法直接运行。对于 Lambda,您还需要将模块与代码库打包。


Zod*_*man 5

我在网络上找到了很多关于执行请求的各种方法的帖子,但没有一个真正展示如何在 AWS Lambda 上同步处理响应。

下面是一个 Node 6.10.3 lambda 函数,它使用 https 请求,收集并返回响应的完整正文,并将控制权传递给未列出的函数processBody以及结果。我相信 http 和 https 在此代码中是可以互换的。

我正在使用异步实用程序模块,这对于新手来说更容易理解。您需要将其推送到 AWS 堆栈才能使用它(我推荐无服务器框架)。

请注意,数据以块的形式返回,这些块聚集在全局变量中,最后在数据完成后调用回调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);
        });
    }
};
Run Code Online (Sandbox Code Playgroud)