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

时间:2015-02-11 08:07:03

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

我开始使用AWS Lambda,并尝试从我的处理程序函数请求外部服务。根据{{​​3}},HTTP请求应该可以正常工作,我还没有找到任何其他文档。 (事实上​​,人们发布了this answer。)

我的处理程序代码是:

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

似乎完全跳过执行请求的处理程序代码的一部分。我从code that use the Twilio API to send SMS开始,然后回到使用普通http来创建一个最小的例子。我还试图请求我控制的服务的URL来检查日志,并且没有请求进入。

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

10 个答案:

答案 0 :(得分:68)

当然,我误解了这个问题。 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

找到完整的文档

答案 1 :(得分:11)

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

<% @econ_reports.includes(:econ_results).each do |econ_report| %>
  <% if econ_report.econ_results.any? %>
    <% last_result = econ_report.econ_results.last %>
    # do something to display last_result
  <% end %>
<% end %>

答案 2 :(得分: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 package(1)中的方法时,它都会被创建为事件,并且此事件会将其作为单独的事件。 'get'函数(2)实际上是这个单独事件的起点。

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

但是在(2)被解雇的事件仍然在某个地方执行,它将花费自己的甜蜜时间来完成。很奇怪,对吧?好吧,不,不是。这就是NodeJS的工作原理,你非常重视这个概念。这是JavaScript Promises提供帮助的地方。

您可以阅读有关JavaScript Promises here的更多信息。简而言之,您需要一个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,您还需要使用代码库打包模块。

答案 3 :(得分:3)

我在网上发现了很多关于执行请求的各种方法的帖子,但实际上并没有显示如何在AWS Lambda上同步处理响应。

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

我正在使用async utility module,这对新手来说更容易理解。您需要将其推送到您的AWS Stack以使用它(我推荐serverless framework)。

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

答案 4 :(得分:1)

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

答案 5 :(得分:0)

enter image description here

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

答案 6 :(得分:0)

使用带有解决拒绝的承诺。它对我有用!

答案 7 :(得分:0)

现代异步/等待示例

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

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

答案 8 :(得分:-1)

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

答案 9 :(得分:-13)

是的,实际上有很多原因可以让您访问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'),也不会起作用,因为此代码运行的地方不是为此而做的。