为什么此HTTP请求在AWS Lambda上不起作用?


89

我开始使用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);
}

我在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

我希望在那里有另一行:

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

但这是缺失的。如果我使用的是本机上节点中没有处理程序包装的基本部分,则代码将按预期工作。

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

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

似乎处理程序代码中执行请求的部分被完全跳过。我从请求库开始,然后回到使用Plainhttp创建一个最小示例。我还尝试请求我控制的服务的URL以检查日志,并且没有任何请求进入。

我完全迷住了。节点和/或AWS Lambda不会执行HTTP请求有什么原因吗?


我认为这可能是由于HTTP请求中缺少用户代理引起的。
Ma'moon Al-Akash 2015年

4
在撰写本文时,这是当前AWS论坛的Lambda论坛中的首要问题。这让我发疯,也让其他人发疯。
Nostradamus

@Nostradamus,我感谢任何其他反馈,更正和支持。将它们发送到这里;-)
awendt

1
我尝试了从Twillo示例到Alexa节点示例捆绑包附带的一些默认示例以及您的context.done()方法的所有内容。http POST无法正常工作。是否可以发布您的POST请求代码的完整示例?
chheplo 2015年

Answers:


79

当然,我误会了这个问题。正如AWS自己所说的那样

对于那些在Lambda中首次遇到nodejs的人来说,一个常见的错误是忘记了回调是异步执行的,而context.done()当您确实打算等待另一个回调(例如S3.PUT操作)完成时,则调用 原始处理程序,从而强制执行该函数因工作不完整而终止。

context.done在请求的任何回调触发之前,我正在调用way,导致我的函数提前终止。

工作代码是这样的:

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)

虽然你仍然会发现succeeddonefail上下文参数,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(),以便调用回调函数。但是您的代码仍然对我不起作用。:(
mabeiyi15年

3
context.done()呼叫需要移动到回调(成功或错误的情况下)。
awendt

2
尚未遇到您的问题,但是当我继续使用lambda时要牢记。
大卫

关于如何从Lambda调用本地系统中的api的任何想法?
阿米特·库玛·戈什

2
用2017年更新来更新2015年问题的道具!
Ace

19

使用节点的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
我花了一个多小时才找到最佳答案,因为node-fetch request默认情况下Lambda上没有libs等。
Alex C

现在,许多示例代码似乎已损坏。这是截至2020年3月的有效示例代码,使用带有Node.js 12.x的AWS Lambda
Muhammad Yussuf

有人可以解释如何使用lambda函数中的数据发出POST请求吗?
帕文都

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

4

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

3

我遇到了同样的问题,然后我意识到NodeJS中的编程实际上与基于JavaScript的Python或Java不同。我将尝试使用简单的概念,因为可能会有一些新人对此感兴趣或会遇到这个问题。

让我们看下面的代码:

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)中的方法进行调用时,该方法都将作为事件创建,并且此事件将获得单独的事件。“获取”功能(2)实际上是此单独事件的起点。

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

但是在(2)处触发的事件仍在某个地方执行,并且需要花费自己的甜蜜时间才能完成。很奇怪,对吧?好吧,不,不是。这就是NodeJS的工作方式,并且围绕这个概念非常重要。这是JavaScript Promises可以提供帮助的地方。

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

大多数常见的NodeJS软件包都有可用的Promised版本的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上同步处理响应。

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

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

请注意,数据以块的形式返回,这些块聚集在全局变量中,最后,当ended数据被调用时,将调用回调。

'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和HTTP Endpoint的原因。

AWS Lambda的架构

这是微服务。通过Amazon Linux AMI(版本3.14.26–24.46.amzn1.x86_64)在EC2内运行,并与Node.js一起运行。内存可以达到128mb和1gb。当数据源触发事件时,详细信息将作为参数传递给Lambda函数。

发生什么事?

AWS Lambda在容器内运行,并且代码与程序包或模块直接上传到此容器。例如,我们永远无法为运行您的lambda函数的linux计算机执行SSH。我们只能监视的日志是CloudWatchLogs以及运行时产生的异常。

AWS负责为我们启动和终止容器,然后运行代码。因此,即使您使用了require('http'),它也不起作用,因为该代码的运行位置并不是为此而设计的。


5
您可能误解了我的问题。我知道Lambda代码在容器中运行,并且我无法访问底层计算机。我既不尝试进入,也不在尝试访问外部端点,而Lambda可以做到这一点。正如我在自己的回答中指出的那样,问题完全是另外一个问题。
awendt
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.