Чому цей запит 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);
}

і я бачу наступні 4 рядки у своїх журналах CloudWatch:

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

Здається, частина обробника коду, що робить запит, повністю пропускається. Я почав з lib запиту і повернувся до використання plain httpдля створення мінімального прикладу. Я також намагався запитати URL-адресу служби, яку я контролюю, щоб перевірити журнали, і запитів не надходить.

Я абсолютно тупий. Чи є причина, по якій Node та / або AWS Lambda не виконують запит HTTP?


Я думаю, що це може бути спричинено відсутністю агента користувача у вашому запиті HTTP.
Ma'moon Al-Akash

4
На момент написання статті це на сьогодні є головним питанням на форумі Лямбда форумів AWS. Це зводить мене з розуму, а також купу інших людей теж.
Нострадамус

@Nostradamus Я вдячний за будь-які додаткові відгуки, виправлення та підтримку. Надішліть їх сюди ;-)
awendt

1
Я спробував все - від прикладу Twillo до кількох прикладів за замовчуванням, що постачаються з набором прикладів вузла Alexa, а також методом context.done (). http POST не працює. Чи можна опублікувати повний зразок коду вашого запиту POST?
чепло

Відповіді:


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


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 році, тепер, коли Лямбда використовує синтаксис await.
Taneem Tee

3
Це зайняло мені більше години, щоб знайти найкращу відповідь, оскільки бібліотеки node-fetch requestтощо не доступні на Lambda за замовчуванням.
Alex C,

Багато зразків коду там, здається, порушено. Це робочий зразок коду станом на березень 2020 року, за допомогою AWS Lambda з Node.js 12.x
Мухаммад Юссуф,

Хтось може пояснити, як робити запити POST із даними всередині лямбда-функцій?
Павінду

11

Так, чудова відповідь ідеальна. Я просто покажу свій робочий код ... Я мав 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();
};

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 (1), він створюється як подія, і ця подія отримує окрему подію. Функція 'get' (2) насправді є початковою точкою цієї окремої події.

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

Але подія, яку обстріляли (2), все ще десь виконується, і її закінчення займе власний солодкий час. Досить химерно, правда? Ну, ні, це не так. Ось як працює NodeJS, і дуже важливо, щоб ви обернули голову навколо цієї концепції. Тут приходять на допомогу JavaScript Promises.

Детальніше про JavaScript Promises можна прочитати тут . У двох словах, вам знадобиться 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 вам також потрібно буде упакувати модулі з кодовою базою.


Так, обіцянки! Хоча я міг би розглянути можливість переходу context.done()виклику на ланцюговий finallyметод.
crftr

3

Я знайшов багато публікацій в Інтернеті про різні способи виконання запиту, але жоден, який насправді показує, як обробити відповідь синхронно на AWS Lambda.

Ось функція лямбда-функції Node 6.10.3, яка використовує запит https, збирає та повертає повне тіло відповіді та передає контроль функції, що не входить до списку, processBodyз результатами. Я вважаю, що http і https взаємозамінні в цьому коді.

Я використовую утилітний модуль async , який легше зрозуміти для початківців. Вам потрібно буде перенести це на ваш AWS Stack, щоб використовувати його (я рекомендую безсерверний фреймворк ).

Зверніть увагу, що дані повертаються шматками, які зібрані в глобальну змінну, і, нарешті, зворотний виклик викликається, коли дані 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> розділ відображення.


-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
Можливо, ви неправильно зрозуміли мою проблему. Я знаю, що лямбда-код запускається в контейнері, і знаю, що не можу отримати доступ до базової машини. Я також не намагаюся потрапити, мій код намагається вибратися, тобто отримати доступ до зовнішніх кінцевих точок, і Лямбда може це зробити досить добре. Проблема полягала в зовсім іншому, на що я вказував у своїй власній відповіді.
awendt
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.