简体   繁体   English

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

[英]Why is this HTTP request not working on AWS Lambda?

I'm getting started with AWS Lambda and I'm trying to request an external service from my handler function.我正在开始使用 AWS Lambda,我正在尝试从我的处理程序函数请求外部服务。 According to this answer , HTTP requests should work just fine, and I haven't found any documentation that says otherwise.根据这个答案,HTTP 请求应该可以正常工作,而且我还没有找到任何其他说明的文档。 (In fact, people have posted code that use the Twilio API to send SMS .) (实际上,人们已经发布了使用 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.txt I'm using is for the invoke-async call 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 lib and fell back to using plain http to create a minimal example.我从请求库开始,然后又回到使用普通http来创建一个最小示例。 I've also tried to request a URL of a service I control to check the logs and there's no requests coming in.我还尝试请求我控制的服务的 URL 来检查日志,但没有请求进来。

I'm totally stumped.我完全被难住了。 Is there any reason Node and/or AWS Lambda would not execute the HTTP request? Node 和/或 AWS Lambda 是否有任何原因不会执行 HTTP 请求?

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 的人来说,一个常见的错误是忘记回调异步执行并在您真正打算等待另一个回调(例如 S3.PUT 操作)时调用原始处理程序中的context.done()完成,强制函数在其工作未完成时终止。

I was calling context.done way before any callbacks for the request fired, causing the termination of my function ahead of time.我在触发请求的任何回调之前调用context.done方式,导致我的函数提前终止。

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).更新:从 2017 年开始,AWS 已弃用旧的 Nodejs 0.10,现在只有较新的 4.3 运行时可用(旧功能应更新)。 This runtime introduced some changes to the handler function.此运行时对处理程序函数进行了一些更改。 The new handler has now 3 parameters.新的处理程序现在有 3 个参数。

function(event, context, callback)

Although you will still find the succeed , done and fail on the context parameter, AWS suggest to use the callback function instead or null is returned by default.尽管您仍然fail在 context 参数上找到succeeddonefail ,但 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找到

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

Yeah, awendt answer is perfect.是的,awendt 答案是完美的。 I'll just show my working code... I had the context.succeed('Blah');我只会展示我的工作代码......我有context.succeed('Blah'); line right after the reqPost.end();reqPost.end()之后的一行; line.线。 Moving it to where I show below solved everything.把它移到我下面展示的地方解决了一切。

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

I faced this issue on Node 10.X version.我在 Node 10.X 版本上遇到了这个问题。 below is my working code.下面是我的工作代码。

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

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

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.每当您调用 http 包 (1) 中的方法时,它都会创建为事件,并且此事件将其作为单独的事件。 The 'get' function (2) is actually the starting point of this separate event. '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.但是在 (2) 处触发的事件仍在某处执行,它需要自己的甜蜜时间才能完成。 Pretty bizarre, right ?.很奇怪,对吧?。 Well, No it is not.嗯,不,不是。 This is how NodeJS works and its pretty important you wrap your head around this concept.这就是 NodeJS 的工作方式,并且非常重要的是您要围绕这个概念。 This is the place where JavaScript Promises come to help.这就是 JavaScript Promise 可以提供帮助的地方。

You can read more about JavaScript Promises here .您可以在此处阅读有关 JavaScript Promise 的更多信息。 In a nutshell, you would need a JavaScript Promise to keep the execution of code inline and will not spawn new / extra threads.简而言之,您需要一个 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.请注意,如果您将其导入 AWS Lambda,上述代码将无法直接运行。 For Lambda, you will need to package the modules with the code base too.对于 Lambda,您还需要将模块与代码库一起打包。

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 processBody with the results.这是一个 Node 6.10.3 lambda 函数,它使用 https 请求,收集并返回响应的完整主体,并将控制权传递给未列出的带有结果的函数processBody I believe http and https are interchangable in this code.我相信 http 和 https 在这段代码中是可以互换的。

I'm using the async utility module , which is easier to understand for newbies.我正在使用async 实用程序模块,这对新手来说更容易理解。 You'll need to push that to your AWS Stack to use it (I recommend the serverless framework ).您需要将其推送到您的 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 end ed.请注意,数据以块的形式返回,这些块收集在一个全局变量中,最后在数据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);
        });
    }
};

Modern Async/Await Example现代异步/等待示例

You need to prevent the lambda from finishing before the https request has completed.您需要在 https 请求完成之前阻止 lambda 完成。 It makes code with multiple requests easier to read as well.它也使具有多个请求的代码更易于阅读。

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

Use promises with resolve reject.使用带有解决拒绝的承诺。 It worked for me!它对我有用!

在此处输入图片说明

Add above code in API gateway under GET-Integration Request> mapping section.在 GET-Integration Request> 映射部分下的 API 网关中添加上述代码。

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.使用 Amazon Linux AMI(版本 3.14.26–24.46.amzn1.x86_64)在 EC2 内运行,并使用 Node.js 运行。 The memory can be beetwen 128mb and 1gb.内存可以是beetwen 128mb 和1gb。 When the data source triggers the event, the details are passed to a Lambda function as parameter's.当数据源触发事件时,详细信息将作为参数传递给 Lambda 函数。

What happen?发生什么事?

AWS Lambda run's inside a container, and the code is directly uploaded to this container with packages or modules. AWS Lambda 在容器内运行,代码直接随包或模块上传到此容器。 For example, we NEVER can do SSH for the linux machine running your lambda function.例如,我们永远无法为运行 lambda 函数的 linux 机器执行 SSH。 The only things that we can monitor are the logs, with CloudWatchLogs and the exception that came from the runtime.我们唯一可以监控的是日志、CloudWatchLogs 和来自运行时的异常。

AWS take care of launch and terminate the containers for us, and just run the code. AWS 负责为我们启动和终止容器,只需运行代码即可。 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.因此,即使您使用 require('http'),它也不会起作用,因为运行此代码的地方不是为此而设计的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM