Почему этот 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"
}

Похоже, что часть кода обработчика, которая выполняет запрос, полностью пропускается. Я начал с библиотеки запросов и вернулся к использованию plain, httpчтобы создать минимальный пример. Я также пытался запросить URL-адрес службы, которую я контролирую, для проверки журналов, но никаких запросов не поступает.

Я в полном тупике. Есть ли причина, по которой Node и / или AWS Lambda не выполняет HTTP-запрос?

Awendt
источник
Я думаю, что это может быть вызвано отсутствием пользовательского агента в вашем HTTP-запросе.
Ma'moon Al-Akash
4
На момент написания это главный вопрос на форуме Lambda форумов AWS. Это сводит с ума меня и многих других.
Нострадамус
@Nostradamus Я ценю любые дополнительные отзывы, исправления и голоса. Присылайте их сюда ;-)
awendt
1
Я пробовал все, от примера Twillo до нескольких примеров по умолчанию, поставляемых с комплектом примеров узла Alexa, а также вашего метода context.done (). http POST не работает. Можно ли опубликовать полный образец кода POST-запроса?
chheplo 05

Ответы:

79

Конечно, я неправильно понял проблему. Как выразились сами AWS :

Для тех, кто впервые сталкивается с nodejs в Lambda, распространенная ошибка заключается в том, что забывают, что обратные вызовы выполняются асинхронно, и вызывают 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)

Хотя вы все еще найти succeed, doneи failна параметр контекста, 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.

Awendt
источник
4
Итак, как заставить работать код обработчика? Насколько я понимаю, вам нужно удалить context.done (), чтобы вызывалась функция обратного вызова. но ваш код все еще не работает для меня. :(
mabeiyi
3
context.done()Вызов должен быть перемещен в функции обратного вызова (для успеха и случае ошибки).
awendt
2
у вас еще не было вашей проблемы, но полезно иметь в виду, когда я продвигаюсь вперед с лямбдой.
Дэвид
любые идеи о том, как я могу вызвать api в моей локальной системе из Lambda?
Амит Кумар Гош,
2
реквизиты для обновления вопроса 2015 года с обновлениями 2017 года!
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 использует синтаксис ожидания.
Taneem Tee
3
На поиск лучшего ответа у меня ушло больше часа, поскольку библиотеки и node-fetch requestт. Д. По умолчанию недоступны в Lambda.
Alex C
Кажется, что большая часть примеров кода сейчас сломана. Это рабочий пример кода по состоянию на март 2020 года с использованием AWS Lambda с Node.js 12.x
Мухаммад Юссуф,
Может ли кто-нибудь объяснить, как делать запросы POST с данными внутри лямбда-функций?
Павинду,
11

Да, ответ awendt идеален. Я просто покажу свой рабочий код ... У меня был context.succeed ('Мля'); строка сразу после 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();
};
imTachu
источник
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 на самом деле отличается от 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 package (1), он создается как событие, и это событие получает отдельное событие. Функция get (2) фактически является отправной точкой этого отдельного события.

Теперь функция в (3) будет выполняться в отдельном событии, и ваш код продолжит выполнение пути и сразу перейдет к (4) и завершит его, потому что больше нечего делать.

Но событие, запущенное в (2), все еще где-то выполняется, и для завершения потребуется свое приятное время. Довольно странно, правда? Ну нет, это не так. Вот как работает NodeJS, и очень важно, чтобы вы осмыслили эту концепцию. Здесь на помощь приходят обещания JavaScript.

Вы можете узнать больше о обещаниях JavaScript здесь . Вкратце, вам понадобится JavaScript Promise, чтобы поддерживать выполнение кода встроенным и не порождать новые / дополнительные потоки.

Для большинства распространенных пакетов NodeJS доступна обещанная версия 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 вам также нужно будет упаковать модули с базой кода.

Mmansoor
источник
Да, обещания! Хотя я бы подумал о переносе context.done()вызова в связанный finallyметод.
crftr 04
3

Я нашел множество сообщений в Интернете о различных способах выполнения запроса, но ни одна из них не показывала, как синхронно обрабатывать ответ на AWS Lambda.

Вот лямбда-функция Node 6.10.3, которая использует запрос https, собирает и возвращает полное тело ответа и передает управление не включенной в список функции processBodyс результатами. Я считаю, что http и https в этом коде взаимозаменяемы.

Я использую служебный модуль async , который легче понять новичкам. Вам нужно будет отправить это в свой стек AWS, чтобы использовать его (я рекомендую бессерверную структуру ).

Обратите внимание, что данные возвращаются кусками, которые собираются в глобальной переменной, и, наконец, обратный вызов вызывается, когда данные 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);
        });
    }
};
Зодман
источник
0

введите описание изображения здесь

Добавьте приведенный выше код в шлюз API в разделе GET-Integration Request> mapping.

Шер Сингх
источник
-14

Да, на самом деле существует множество причин, по которым вы можете получить доступ к AWS Lambda like и HTTP Endpoint.

Архитектура AWS Lambda

Это микросервис. Работает внутри EC2 с Amazon Linux AMI (версии 3.14.26–24.46.amzn1.x86_64) и работает с Node.js. Память может быть от 128 МБ до 1 ГБ. Когда источник данных запускает событие, подробности передаются в лямбда-функцию как параметры.

Что случилось?

AWS Lambda выполняется внутри контейнера, и код напрямую загружается в этот контейнер с пакетами или модулями. Например, мы НИКОГДА не можем использовать SSH для Linux-машины, на которой выполняется ваша лямбда-функция. Единственное, что мы можем отслеживать, - это журналы с CloudWatchLogs и исключение, пришедшее из среды выполнения.

AWS позаботится о запуске и остановке контейнеров за нас, а просто запустите код. Итак, даже если вы используете require ('http'), это не сработает, потому что место, где запускается этот код, не предназначено для этого.

Джонатанбаральди
источник
5
Возможно, вы неправильно поняли мою проблему. Я знаю, что код Lambda выполняется в контейнере, и знаю, что не могу получить доступ к базовой машине. Я тоже не пытаюсь войти, мой код пытается выйти, то есть получить доступ к внешним конечным точкам, и Lambda может делать это довольно хорошо. Проблема заключалась в другом, как я указал в своем ответе.
awendt