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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28449363/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 18:25:23  来源:igfitidea点击:

Why is this HTTP request not working on AWS Lambda?

node.jsamazon-web-serviceshttprequestaws-lambda

提问by awendt

I'm getting started with AWS Lambda and I'm trying to request an external service from my handler function. According to this answer, HTTP requests should work just fine, and I haven't found any documentation that says otherwise. (In fact, people have posted code that use the Twilio API to send SMS.)

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

My handler code is:

我的处理程序代码是:

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

and I see the following 4 lines in my CloudWatch logs:

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

I'd expect another line in there:

我希望那里有另一行:

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

but that's missing. If I'm using the essential part without the handler wrapper in node on my local machine, the code works as expected.

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

The inputfile.txtI'm using is for the invoke-asynccall is this:

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

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

It seems like the part of the handler code that does the request is skipped entirely. I started out with the request liband fell back to using plain httpto create a minimal example. I've also tried to request a URL of a service I control to check the logs and there's no requests coming in.

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

I'm totally stumped. Is there any reason Node and/or AWS Lambda would not execute the HTTP request?

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

采纳答案by awendt

Of course, I was misunderstanding the problem. As AWS themselves put it:

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

For those encountering nodejs for the first time in Lambda, a common error is forgetting that callbacks execute asynchronously and calling context.done()in the original handler when you really meant to wait for another callback (such as an S3.PUT operation) to complete, forcing the function to terminate with its work incomplete.

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

I was calling context.doneway before any callbacks for the request fired, causing the termination of my function ahead of time.

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

The working code is this:

工作代码是这样的:

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

Update:starting 2017 AWS has deprecated the old Nodejs 0.10 and only the newer 4.3 run-time is now available (old functions should be updated). This runtime introduced some changes to the handler function. The new handler has now 3 parameters.

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

function(event, context, callback)

Although you will still find the succeed, doneand failon the context parameter, AWS suggest to use the callbackfunction instead or nullis returned by default.

尽管您仍会在上下文参数上找到succeed,donefail,但 AWS 建议改用该callback函数或null默认返回该函数。

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

Complete documentation can be found at http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html

完整的文档可以在http://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html找到

回答by smsivaprakaash

Simple Working Example of Http request using node.

使用节点的 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();
    });
}

回答by imTachu

Yeah, awendt answer is perfect. I'll just show my working code... I had the context.succeed('Blah');line right after the reqPost.end();line. Moving it to where I show below solved everything.

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

回答by Ameya Salagre

I faced this issue on Node 10.X version. below is my working code.

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

回答by mmansoor

I had the very same problem and then I realized that programming in NodeJS is actually different than Python or Java as its based on JavaScript. I'll try to use simple concepts as there may be a few new folks that would be interested or may come to this question.

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

Let's look at the following code :

让我们看看下面的代码:

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

Whenever you make a call to a method in http package (1) , it is created as event and this event gets it separate event. The 'get' function (2) is actually the starting point of this separate event.

每当您调用 http 包 (1) 中的方法时,它都会创建为事件,并且此事件将其作为单独的事件。'get' 函数 (2) 实际上是这个单独事件的起点。

Now, the function at (3) will be executing in a separate event, and your code will continue it executing path and will straight jump to (4) and finish it off, because there is nothing more to do.

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

But the event fired at (2) is still executing somewhere and it will take its own sweet time to finish. Pretty bizarre, right ?. Well, No it is not. This is how NodeJS works and its pretty important you wrap your head around this concept. This is the place where JavaScript Promises come to help.

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

You can read more about JavaScript Promises here. In a nutshell, you would need a JavaScript Promise to keep the execution of code inline and will not spawn new / extra threads.

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

Most of the common NodeJS packages have a Promised version of their API available, but there are other approaches like BlueBirdJS that address the similar problem.

大多数常见的 NodeJS 包都有可用的 API 的承诺版本,但还有其他方法,如 BlueBirdJS,可以解决类似的问题。

The code that you had written above can be loosely re-written as follows.

您在上面编写的代码可以松散地重写如下。

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

Please note that the above code will not work directly if you will import it in AWS Lambda. For Lambda, you will need to package the modules with the code base too.

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

回答by Zodman

I've found lots of posts across the web on the various ways to do the request, but none that actually show how to process the response synchronously on AWS Lambda.

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

Here's a Node 6.10.3 lambda function that uses an https request, collects and returns the full body of the response, and passes control to an unlisted function processBodywith the results. I believe http and https are interchangable in this code.

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

I'm using the async utility module, which is easier to understand for newbies. You'll need to push that to your AWS Stack to use it (I recommend the serverless framework).

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

Note that the data comes back in chunks, which are gathered in a global variable, and finally the callback is called when the data has ended.

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

回答by jonathanbaraldi

Yes, there's in fact many reasons why you can access AWS Lambda like and HTTP Endpoint.

是的,实际上有很多原因可以访问 AWS Lambda 之类的和 HTTP 端点。

The architecture of AWS Lambda

AWS Lambda 的架构

It's a microservice. Running inside EC2 with Amazon Linux AMI (Version 3.14.26–24.46.amzn1.x86_64) and runs with Node.js. The memory can be beetwen 128mb and 1gb. When the data source triggers the event, the details are passed to a Lambda function as parameter's.

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

What happen?

发生什么事?

AWS Lambda run's inside a container, and the code is directly uploaded to this container with packages or modules. For example, we NEVER can do SSH for the linux machine running your lambda function. The only things that we can monitor are the logs, with CloudWatchLogs and the exception that came from the runtime.

AWS Lambda 在容器内运行,代码直接随包或模块上传到此容器。例如,我们永远无法为运行 lambda 函数的 linux 机器执行 SSH。我们唯一可以监控的是日志、CloudWatchLogs 和来自运行时的异常。

AWS take care of launch and terminate the containers for us, and just run the code. So, even that you use require('http'), it's not going to work, because the place where this code runs, wasn't made for this.

AWS 负责为我们启动和终止容器,只需运行代码即可。因此,即使您使用 require('http'),它也不会起作用,因为运行此代码的地方不是为此而设计的。