AWS Lambda, похоже, завершает работу до завершения - PullRequest
0 голосов
/ 11 января 2019

У меня есть очень простая лямбда-функция (nodeJS), которая помещает полученное событие в поток кинезиса. Вот исходный код:


    'use strict';

    const AWS = require('aws-sdk');
    const kinesis = new AWS.Kinesis({apiVersion: '2013-12-02'});

    exports.handler = async (event, context, callback) => {
        let body = JSON.parse(event.body);
        let receptionDate = new Date().toISOString();
        let partitionKey = "pKey-" + Math.floor(Math.random() * 10);

        // Response format needed for API Gateway
        const formatResponse = (status, responseBody) => {
            return {
                statusCode: status,
                headers: { "Content-Type": "application/json" },
                body: JSON.stringify(responseBody)
            }
        }

        // body.events is an array of events. Just add the reception date in each events.
        for(let e of body.events) {
            e.reception_date = receptionDate;
        }

        console.log("put In kinesis stream");
        let kinesisParams = {
            Data: new Buffer(JSON.stringify(body) + "\n"),
            PartitionKey: partitionKey,
            StreamName: 'event_test'
        };

        kinesis.putRecord(kinesisParams, (err, res) => {
            console.log("Kinesis.putRecord DONE");
            if(err) {
                console.log("putRecord Error:", JSON.stringify(err));
                callback(null, formatResponse(500, "Internal Error: " + JSON.stringify(err)));
            } else {
                console.log("putRecord Success:", JSON.stringify(res));
                callback(null, formatResponse(200));
            }
        });
    };

Когда этот код выполняется, вот логи в cloudwatch:

START RequestId: 5d4d7526-1a40-401f-8417-06435f0e5408 Version: $LATEST
2019-01-11T09:39:11.925Z    5d4d7526-1a40-401f-8417-06435f0e5408    put In kinesis stream
END RequestId: 5d4d7526-1a40-401f-8417-06435f0e5408
REPORT RequestId: 5d4d7526-1a40-401f-8417-06435f0e5408  Duration: 519.65 ms Billed Duration: 600 ms     Memory Size: 128 MB Max Memory Used: 28 MB  

Кажется, что kinesis.putRecord не вызывается ... Я ничего не вижу в логах потока kinesis. Я конечно где-то не прав, но я не знаю где!

Ответы [ 3 ]

0 голосов
/ 13 января 2019

kinesis.putRecord - это асинхронная операция, которая вызывает функцию обратного вызова (второй параметр), когда она завершена (успешно или с ошибкой).

async функция - это функция, которая возвращает обещание. Лямбда завершит свое выполнение, когда это обещание будет выполнено, даже если есть другие асинхронные операции, которые еще не выполнены. Так как ваша функция ничего не возвращает, то обещание немедленно разрешается, когда функция завершается, и поэтому выполнение будет немедленно завершено - без ожидания вашей асинхронной kinesis.putRecord задачи.

При использовании обработчика async вам не нужно вызывать обратный вызов. Вместо этого вы возвращаете то, что хотите, или выкидываете ошибку. Лямбда получит это и ответит соответственно.

Итак, у вас есть 2 варианта:

  1. Поскольку в вашем коде нет await, просто удалите async. В этом случае Lambda ожидает, что цикл событий будет emtpy ( Если вы явно не измените context.callbackWaitsForEmptyEventLoop )
  2. Измените kinesis.putRecord на что-то вроде:
let result;

try {
  result = await kinesis.putRecord(kinesisParams).promise();
} catch (err) {
  console.log("putRecord Error:", JSON.stringify(err));
  throw Error(formatResponse(500, "Internal Error: " + JSON.stringify(err));
}

console.log("putRecord Success:", JSON.stringify(result));
return formatResponse(200);

Во втором варианте лямбда будет работать до тех пор, пока kinesis.putRecord не будет завершен.

Для получения дополнительной информации о поведении Lambda в этом случае, вы можете увидеть основной код, который выполняет ваш обработчик в /var/runtime/node_modules/awslambda/index.js в контейнере Lambda.

0 голосов
/ 29 января 2019

@ ttulka не могли бы вы объяснить немного больше? Даете советы или образцы кода? - Adagyo

Речь идет об эволюции асинхронной обработки в JavaScript.

Во-первых, все было сделано с обратным вызовом, это самый старый подход. Повсеместное использование обратных вызовов приводит к «Аду обратного вызова» (http://callbackhell.com).

Затем был представлен Обещания. Работа с Promises напоминает работу с Monads, все упаковано в «коробку» (Promise), поэтому вы должны объединить все ваши звонки:

thisCallReturnsPromise(...)
  .then(data => ...)
  .then(data => ...)
  .then(data => ...)
  .catch(err => ...)

Это немного неестественно для людей, поэтому ECMAScript 2017 предложил синтаксический сахар в асинхронных функциях (async / await) https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

Асинхронный / ожидающий синтаксис позволяет работать с асинхронными обещаниями, как с обычным кодом синхронизации:

const data = await thisCallReturnsPromise(...)

Не забывайте, что вызов await должен быть внутри асинхронной функции:

async () => {
  const data = await thisCallReturnsPromise(...)
  return await processDataAsynchronouslyInPromise(data)
}

AWS Lambda поддерживает Node.js v8.10, который полностью реализует этот синтаксис.

0 голосов
/ 11 января 2019

Только что нашел решение: удаление ключевого слова "async" заставит его работать!

    exports.handler = (event, context, callback) => { ... }
...