Почему этот HTTP-запрос не работает на AWS Lambda?
Я начинаю работу с 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
но это отсутствует. Если я использую основную часть без оболочки обработчика в node на моей локальной машине, код работает как ожидалось.
inputfile.txt
Я использую для вызова invoke-async
следующее:
{
"url":"http://www.google.com"
}
Кажется, что часть кода обработчика, которая выполняет запрос, полностью пропущена. Я начал с request lib и вернулся к использованию простого http
, чтобы создать минимальный пример. Я также пытался запросить URL-адрес службы, которую я контролирую, для проверки журналов, и там не поступают запросы.
Я полностью в тупике. Есть ли какая-либо причина node и/или AWS Lambda не будет выполнять HTTP-запрос?
Ответы
Ответ 1
Конечно, я не понял эту проблему. Как сами 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
Ответ 2
Да, ответ 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();
};
Ответ 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-интерфейса Promised, но есть и другие подходы, такие как 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 вам также нужно будет упаковать модули с базой кода.
Ответ 4
В Интернете я обнаружил множество сообщений о различных способах выполнения запроса, но ни одно из них не показывало, как обрабатывать ответ синхронно в 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);
});
}
};
Ответ 5
Если вас не волнует результат вашего http-вызова (возможно, потому что это надежный сервис, такой как конечная точка API для облачных функций firebase, и случайный сбой не будет иметь значения, так как это периодическая работа по очистке), тогда Ваш код может быть значительно упрощен:
const https = require('https');
exports.handler = (event, context, callback) => {
console.log("Invoking GCP.");
https.get('https://us-central1-your-app.cloudfunctions.net/runPeriodicCleanup');
console.log("GCP invoked.");
};
Ответ 6
Простой рабочий пример запроса 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();
});
}
Ответ 7
Да, на самом деле есть много причин, по которым вы можете получить доступ к AWS Lambda like и HTTP Endpoint.
Архитектура AWS Lambda
Это микросервис. Запуск внутри EC2 с Amazon Linux AMI (версия 3.14.26-24.46.amzn1.x86_64) и работает с Node.js. Память может быть равна 128 МБ и 1 ГБ. Когда источник данных запускает событие, детали передаются в функцию Lambda в качестве параметров.
Что произойдет?
AWS Lambda запускается внутри контейнера, и код непосредственно загружается в этот контейнер с пакетами или модулями. Например, мы НИКОГДА не можем делать SSH для Linux-машины с вашей лямбда-функцией. Единственное, что мы можем отслеживать, это журналы, с CloudWatchLogs и исключение, которое появилось из среды выполнения.
AWS позаботится о запуске и завершении контейнеров для нас, и просто запустите код. Таким образом, даже если вы используете require ( "http" ), он не будет работать, потому что место, где выполняется этот код, не было сделано для этого.