【问题标题】:Why is this HTTP request not working on AWS Lambda?为什么这个 HTTP 请求在 AWS Lambda 上不起作用?
【发布时间】:2015-04-11 12:42:39
【问题描述】:

我开始使用 AWS Lambda,并尝试从我的处理程序函数请求外部服务。根据this answer 的说法,HTTP 请求应该可以正常工作,而且我还没有找到任何其他说明的文档。 (其实已经有人发了code that use the Twilio API to send 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"
}

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

我完全被难住了。 Node 和/或 AWS Lambda 是否有任何原因不执行 HTTP 请求?

【问题讨论】:

  • 我认为这可能是由于您的 HTTP 请求中缺少用户代理造成的。
  • 在撰写本文时,这是目前 AWS 论坛的 Lambda 论坛中的热门问题。这让我和其他一群人都快疯了。
  • @Nostradamus 感谢任何额外的反馈、更正和支持。将它们发送到这里 ;-)
  • 我尝试了从 Twillo 示例到 Alexa 节点示例包附带的几个默认示例以及您的 context.done() 方法的所有内容。 http POST 不工作。是否可以发布您的 POST 请求代码的完整示例?

标签: node.js amazon-web-services httprequest aws-lambda


【解决方案1】:

当然,我误解了这个问题。 As AWS themselves put it:

对于那些第一次在 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);
}

更新: 从 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找到

【讨论】:

  • 那么,如何使处理程序代码工作?我的理解是您需要删除 context.done() 以便调用回调函数。但是您的代码仍然对我不起作用。 :(
  • context.done() 调用需要移动到回调中(用于成功和错误情况)。
  • 还没有遇到您的问题,但是在我继续使用 lambda 时请记住这一点。
  • 关于如何从 Lambda 调用本地系统中的 api 的任何想法?
  • 用 2017 年更新更新 2015 年问题的道具!
【解决方案2】:

是的,事实上,您可以访问 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'),它也不会工作,因为这段代码运行的地方不是为此而设计的。

【讨论】:

  • 你可能误解了我的问题。我知道 Lambda 代码在容器中运行,并且我知道我无法访问底层机器。我也没有尝试进入,我的代码正在尝试退出,即访问外部端点,而 Lambda 可以很好地做到这一点。正如我在自己的回答中指出的那样,问题完全是另外一回事。
【解决方案3】:

是的,awendt 的答案是完美的。我将只显示我的工作代码...我在 reqPost.end(); 行之后有 context.succeed('Blah'); 行。将它移到我在下面显示的位置解决了所有问题。

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】:

    我遇到了同样的问题,然后我意识到 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 Promises here 的信息。简而言之,您需要一个 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);
    };
    

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

    【讨论】:

    • 是的,承诺!虽然我会考虑将context.done() 调用移动到链接的finally 方法中。
    【解决方案5】:

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

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

    我使用的是async utility module,对于新手来说更容易理解。您需要将其推送到您的 AWS 堆栈才能使用它(我推荐 serverless framework)。

    请注意,数据以块的形式返回,这些块聚集在一个全局变量中,最后当数据具有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);
            });
        }
    };
    

    【讨论】:

      【解决方案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();
          });
      }
      

      【讨论】:

      • 谢谢你。这是我在 2019 年在此页面上看到的最佳答案,因为 Lambda 正在使用 await 语法。
      • 这花了我一个多小时才找到最佳答案,因为库 node-fetch request 等默认情况下在 Lambda 上不可用。
      • 现在很多示例代码似乎都被破坏了。这是截至 2020 年 3 月的工作示例代码,使用 AWS Lambda 和 Node.js 12.x
      • 有人可以解释如何使用 lambda 函数中的数据发出 POST 请求吗?
      • 这真是救命啊。非常感谢
      【解决方案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();
      };
      

      【讨论】:

        【解决方案8】:

        在 GET-Integration Request> 映射部分下的 API 网关中添加上述代码。

        【讨论】:

          【解决方案9】:

          使用带有resolve reject的promise。它对我有用!

          【讨论】:

            【解决方案10】:

            现代异步/等待示例

            您需要防止 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);
                }
            };
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2018-06-05
              • 2020-10-15
              • 1970-01-01
              • 1970-01-01
              • 2021-02-16
              • 2020-09-19
              • 2018-09-20
              相关资源
              最近更新 更多