Как вы структурируете последовательные вызовы службы AWS в лямбда, учитывая, что все вызовы асинхронны?
Я прихожу из java-фона, поэтому немного новичка в Javascript-соглашениях, необходимых для Lambda.
У меня есть функция лямбда, которая предназначена для выполнения нескольких задач AWS в определенном порядке, в зависимости от результата предыдущей задачи.
Учитывая, что каждая задача выводит свои результаты асинхронно, мне интересно, правильно ли убедиться, что все они происходят в правильной последовательности, а результаты одной операции доступны для вызова следующей функции.
Кажется, что мне нужно вызывать каждую функцию в обратном вызове предыдущей функции, но похоже, что это будет какая-то глубокая вложенность и интересно, правильно ли это сделать.
Например, для этих функций требуется DynamoDB getItem, следуя вызовом SNS для получения конечной точки, за которым следует вызов SNS для отправки сообщения, за которым следует запись DynamoDB.
Какой правильный способ сделать это в лямбда-javascript, учитывающий всю эту асинхронность?
Ответы
Ответ 1
Мне нравится ответ от @jonathanbaraldi, но я думаю, было бы лучше, если бы вы управляли потоком управления с помощью Promises. Библиотека Q имеет некоторые удобные функции, такие как nbind
, которые помогают преобразовать API обратного вызова стиля node как aws-sdk в Promises.
Итак, в этом примере я пришлю электронное письмо, а затем, как только ответ электронной почты вернется, я пришлю второе письмо. По сути, это было задано, вызывая несколько сервисов последовательно. Я использую метод then
promises, чтобы управлять этим по вертикально читаемому пути. Также используйте catch
для обработки ошибок. Я думаю, что он читает намного лучше, просто просто вставляя функции обратного вызова.
var Q = require('q');
var AWS = require('aws-sdk');
AWS.config.credentials = { "accessKeyId": "AAAA","secretAccessKey": "BBBB"};
AWS.config.region = 'us-east-1';
// Use a promised version of sendEmail
var ses = new AWS.SES({apiVersion: '2010-12-01'});
var sendEmail = Q.nbind(ses.sendEmail, ses);
exports.handler = function(event, context) {
console.log(event.nome);
console.log(event.email);
console.log(event.mensagem);
var nome = event.nome;
var email = event.email;
var mensagem = event.mensagem;
var to = ['[email protected]'];
var from = '[email protected]';
// Send email
mensagem = ""+nome+"||"+email+"||"+mensagem+"";
console.log(mensagem);
var params = {
Source: from,
Destination: { ToAddresses: to },
Message: {
Subject: {
Data: 'Form contact our Site'
},
Body: {
Text: {
Data: mensagem,
}
}
};
// Here is the white-meat of the program right here.
sendEmail(params)
.then(sendAnotherEmail)
.then(success)
.catch(logErrors);
function sendAnotherEmail(data) {
console.log("FIRST EMAIL SENT="+data);
// send a second one.
return sendEmail(params);
}
function logErrors(err) {
console.log("ERROR="+err, err.stack);
context.done();
}
function success(data) {
console.log("SECOND EMAIL SENT="+data);
context.done();
}
}
Ответ 2
Я не знаю Lambda, но вы должны заглянуть в асинхронную библиотеку node как способ последовательности асинхронных функций.
async упростил мою жизнь, а мой код стал гораздо более упорядоченным без проблемы с глубиной вложенности в ваш вопрос.
Типичный асинхронный код может выглядеть так:
async.waterfall([
function doTheFirstThing(callback) {
db.somecollection.find({}).toArray(callback);
},
function useresult(dbFindResult, callback) {
do some other stuff (could be synch or async)
etc etc etc
callback(null);
],
function (err) {
//this last function runs anytime any callback has an error, or if no error
// then when the last function in the array above invokes callback.
if (err) { sendForTheCodeDoctor(); }
});
Посмотрите на async doco по ссылке выше. Существует множество полезных функций для последовательных, параллельных, водопадов и многих других. Async активно поддерживается и кажется очень надежным.
Удачи!
Ответ 3
Очень специфическое решение, которое приходит на ум, - это каскадные вызовы лямбда. Например, вы можете написать:
- Функция Lambda получает что-то из DynamoDB, затем вызывает...
- ... функция Lambda, которая вызывает SNS для получения конечной точки, затем вызывает...
- ... функция Lambda, которая отправляет сообщение через SNS, затем вызывает...
- ... Лямбда-функция, которая записывает в DynamoDB
Все эти функции берут вывод из предыдущей функции в качестве входного. Это, конечно, очень тонко, и вы можете решить сгруппировать определенные звонки. Выполнение этого способа позволяет избежать обратного вызова ада в вашем JS-коде как минимум.
(Как примечание, я не уверен, насколько хорошо DynamoDB интегрируется с Lambda. AWS может генерировать события изменения для записей, которые затем могут обрабатываться через Lambda.)
Ответ 4
Я нашел эту статью, которая, кажется, имеет ответ в собственном javascript.
Пять шаблонов, которые помогут вам приручить javascript asynchronis.
Ответ 5
Я хотел бы предложить следующее решение, которое просто создает вложенную функциональную структуру.
// start with the last action
var next = function() { context.succeed(); };
// for every new function, pass it the old one
next = (function(param1, param2, next) {
return function() { serviceCall(param1, param2, next); };
})("x", "y", next);
Это делается для копирования всех переменных для вызова функции, которую вы хотите создать, а затем вложенных в предыдущий вызов. Вы захотите планировать свои события назад. Это действительно так же, как создание пирамиды обратных вызовов, но работает, когда вы заранее не знаете структуру или количество вызовов функций. Вы должны обернуть функцию в закрытии, чтобы скопировать правильное значение.
Таким образом, я могу упорядочить сервисные вызовы AWS, чтобы они шли 1-2-3 и заканчивались закрытием контекста. Предположительно, вы могли бы также структурировать его как стек вместо этой псевдорекурсии.
Ответ 6
Просто увидел этот старый поток. Обратите внимание, что будущие версии JS улучшат это. Взгляните на синтаксис ES2017 async/await, который упрощает асинхронный вложенный вызов в чистый код синхронизации.
Теперь есть несколько polyfills, которые могут предоставить вам эту функциональность на основе синтаксиса ES2016.
Как последний FYI-AWS Lambda теперь поддерживает .Net Core, который предоставляет этот чистый синтаксис асинхронных сообщений из коробки.
Ответ 7
По умолчанию Javascript является асинхронным.
Итак, все, что вам нужно сделать, не использовать эти библиотеки, вы можете, но есть простые способы решить эту проблему. В этом коде я отправил электронное письмо с данными, которые поступают из события, но если вы хотите, вам просто нужно добавить больше функций внутри функций.
Важно то, где ваш context.done(); будет, он собирается закончить вашу функцию лямбда. Вам нужно поставить его в конец последней функции.
var AWS = require('aws-sdk');
AWS.config.credentials = { "accessKeyId": "AAAA","secretAccessKey": "BBBB"};
AWS.config.region = 'us-east-1';
var ses = new AWS.SES({apiVersion: '2010-12-01'});
exports.handler = function(event, context) {
console.log(event.nome);
console.log(event.email);
console.log(event.mensagem);
nome = event.nome;
email = event.email;
mensagem = event.mensagem;
var to = ['[email protected]'];
var from = '[email protected]';
// Send email
mensagem = ""+nome+"||"+email+"||"+mensagem+"";
console.log(mensagem);
ses.sendEmail( {
Source: from,
Destination: { ToAddresses: to },
Message: {
Subject: {
Data: 'Form contact our Site'
},
Body: {
Text: {
Data: mensagem,
}
}
}
},
function(err, data) {
if (err) {
console.log("ERROR="+err, err.stack);
context.done();
} else {
console.log("EMAIL SENT="+data);
context.done();
}
});
}