Лямбда-функция занимает> 3 секунды для запуска + разогрев 5-10 секунд каждый раз - PullRequest
0 голосов
/ 16 июня 2019

У меня есть простая функция node.js с 2 вызовами API REST и выходом сокетного соединения, размещенным в лямбда-выражении AWS.Это занимает 5-10 секунд времени прогрева и> 3+ секунд времени выполнения.

Когда код запускается локально, он выполняет оба запроса, подключение к сокету и завершается примерно через ~ 1300 мс.Почему AWS более чем вдвое увеличивает время выполнения?Я установил тайм-аут на 120 секунд и память на 128 МБ (по умолчанию).

Я ценю, что код не очень аккуратный;Я работаю над его очисткой, но пока что-то нужно.

Проект просто получает информацию из ServiceM8 через API при вызове по подписке webhook, затем форматирует информацию в строки ZPL и перенаправляет их в tcpсервер для печати через термопринтер.

У меня такие вопросы:

  1. Это мой код для бутылочки?
  2. Можно ли оптимизировать его работу быстрее?
  3. Нужно ли мне просто использовать согревающий плагин для моей функции, чтобы разрешить горячий запуск?

Моя функция:

'use strict';
//Require libraries
var request = require("request");
var net = require('net');

exports.handler = (event, context, callback) => {
    if (event.eventName != 'webhook_subscription') { 
        callback(null, {});
    }


    //Global Variables
    var strAssetUUID;
    var strAssetURL;
    var strFormUUID;
    var strTestDate;
    var strRetestDate;
    var appliancePass = true;
    var strAccessToken;
    var strResponseUUID;

    //Printer Access
    const tcpUrl = 'example.com';
    const tcpPort = 12345;
    var client = new net.Socket();

    //UUID of Appliance Test Form.
    const strTestFormUUID = 'UUID_of_form';

//Begin function

    /**
     * Inspect the `eventArgs.entry` argument to get details of the change that caused the webhook
     * to fire.
     */
    strResponseUUID = event.eventArgs.entry[0].uuid;
    strAccessToken = event.auth.accessToken;

    console.log('Response UUID: ' + strResponseUUID);
    console.log('Access Token: ' + strAccessToken);

    //URL Options for FormResponse UUID query
    const urlFormResponse = {  
        url: 'https://api.servicem8.com/api_1.0/formresponse.json?%24filter=uuid%20eq%20' + strResponseUUID,
        headers: {
            // Use the temporary Access Token that was issued for this event
            'Authorization': 'Bearer ' + strAccessToken
        }
    };

//Query form Response UUID to get information required.
request.get(urlFormResponse, function(err, res, body) {
    //Check response code from API query
    if (res.statusCode != 200) {
        // Unable to query form response records
        callback(null, {err: "Unable to query form response records, received HTTP " + res.statusCode + "\n\n" + body});
        return;
        }
    //If we do recieve a 200 status code, begin 
    var arrRecords = JSON.parse(body);

    //Store the UUID of the form used for the form response.
    strFormUUID = arrRecords[0].form_uuid;
    console.log('Form UUID: ' + strFormUUID);

    //Store the UUID of the asset the form response relates to.
    strAssetUUID = arrRecords[0].asset_uuid;
    console.log('Asset UUID: ' + strAssetUUID);

    if (strFormUUID == strTestFormUUID){
            //Get the edited date and parse it into a JSON date object.
            var strEditDate = new Date(arrRecords[0].edit_date);
            //Reassemble JSON date to dd-mm-yyyy.
            strTestDate = strEditDate.getDate() + '/' + (strEditDate.getMonth() + 1) + '/' + strEditDate.getFullYear();
            //Extract the response for retest period.
            var strRetestAnswer = JSON.parse(arrRecords[0].field_data);
            strRetestAnswer = strRetestAnswer[0].Response;
            //Appropriate function based on retest response.
            switch(strRetestAnswer) {
                case '3 Months':
                    //Add x months to current test date object
                    strEditDate.setMonth(strEditDate.getMonth() + 3);
                    strRetestDate = strEditDate.getDate() + '/' + (strEditDate.getMonth() + 1) + '/' + strEditDate.getFullYear();
                    break;
                case '6 Months':
                    strEditDate.setMonth(strEditDate.getMonth() + 6);
                    strRetestDate = strEditDate.getDate() + '/' + (strEditDate.getMonth() + 1) + '/' + strEditDate.getFullYear();
                    break;
                case '12 Months':
                    strEditDate.setMonth(strEditDate.getMonth() + 12);
                    strRetestDate = strEditDate.getDate() + '/' + (strEditDate.getMonth() + 1) + '/' + strEditDate.getFullYear();
                    break;
                case '2 Years':
                    strEditDate.setMonth(strEditDate.getMonth() + 24);
                    strRetestDate = strEditDate.getDate() + '/' + (strEditDate.getMonth() + 1) + '/' + strEditDate.getFullYear();
                    break;
                case '5 Years':
                    strEditDate.setMonth(strEditDate.getMonth() + 60);
                    strRetestDate = strEditDate.getDate() + '/' + (strEditDate.getMonth() + 1) + '/' + strEditDate.getFullYear();
                    break;
                default:
                    strRetestDate = "FAIL";
                    appliancePass = false;
            }

            console.log('Appliance Pass: ' + appliancePass);
            console.log('Test Date: ' + strTestDate);
            console.log('Retest Period: ' + strRetestAnswer);
            console.log('Retest Date: ' + strRetestDate);

            //URL Options for Asset UUID query
            const urlAssetResponse = {
            url: 'https://api.servicem8.com/api_1.0/asset/' + strAssetUUID + '.json',
            headers: {
                // Use the temporary Access Token that was issued for this event
                'Authorization': 'Bearer ' + strAccessToken
                }
            };

            //Query the api for the asset URL of the provided asset UUID.
            request.get(urlAssetResponse, function(err, res, body) {
                //Check response code from API query
                if (res.statusCode != 200) {
                    // Unable to query asset records
                    callback(null, {err: "Unable to query asset records, received HTTP " + res.statusCode + "\n\n" + body});
                    return;
                }
                //If we do recieve a 200 status code, begin 
                var strAssetResponse = JSON.parse(body);
                //Store the asset URL
                strAssetURL = 'https://sm8.io/' + strAssetResponse.asset_code;
                console.log('Asset URL: ' + strAssetURL);
            //generate tag and send to printer
            var strZPLPass = ('^XA....^XZ\n');
            var strZPLFail = ('^XA....^XZ\n');
                            //Now that we have our ZPL generated from our dates and URLs
                            //Send the correct ZPL to the printer.
                            client.connect(tcpPort, tcpUrl, function() {
                                console.log('Connected');
                                //Send Appropriate ZPL
                                if (appliancePass) {
                                    client.write(strZPLPass);
                                }else {
                                    client.write(strZPLFail);
                                }
                                console.log('Tag Successfully Printed!');
                                //As the tcp server receiving the string does not return any communication
                                //there is no way to know when the data has been succesfully received in full.
                                //So we simply timeout the connection after 750ms which is generally long enough
                                //to ensure complete transmission.
                                setTimeout(function () {
                                    console.log('Timeout, connection closing...');
                                    client.destroy();
                                    }, 750);
                                });
            });
    }
});
};

Ответы [ 2 ]

1 голос
/ 16 июня 2019

Прежде всего, я бы посоветовал вам прекратить использование модуля request и перейти на нативный. В наши дни все можно сделать без тонны строк. request - модуль с 48 зависимостями; если вы делаете математику, то это тысячи строк для простого запроса GET.

Вы всегда должны минимизировать сложность своих зависимостей. Я использую Lambda для проверки работоспособности своих сайтов, собирая весь запрос и проверяя HTML на совершенно разных серверах . VPS находится во Франкфурте, AWS в Ирландии. Мой мс / запрос находится в диапазоне от 100 до 150 мс.

Вот простой запрос обещания, который я использую:

function request(obj, timeout) {
    return new Promise(function(res, rej) {
        if (typeof obj !== "object") {
            rej("Argument must be a valid http request options object")
        }
        obj.timeout = timeout;
        obj.rejectUnauthorized = false;
        let request = http.get(obj, (response) => {
            if (response.statusCode !== 200) {
                rej("Connection error");
            }
            var body = '';
            response.on('data', (chunk) => {
                body += chunk;
            });
            response.on('end', () => {
                res(body);
            });
            response.on('error', (error) => {
                rej(error);
            });
        });

        request.setTimeout(timeout);
        request.on('error', (error) => {
            rej(error);
        })
        request.on('timeout', () => {
            request.abort();
            rej("Timeout!")
        })
    });
}

Пример

const reqOpts = {
    hostname: 'www.example.com',
    port: 443,
    path: '/hello',
    method: 'GET',
    headers: {
        handshake: "eXTNxFMxQL4pRrj6JfzQycn3obHL",
        remoteIpAddress: event.sourceIp || "lambda"
    }
}
try {
    httpTestCall = await request(reqOpts, 250);
}
catch (e) {
    console.error(e);
}

Теперь, основываясь на этом изменении, переключите ваш обработчик в асинхронный режим, используя exports.handler = async(event, context, callback) => {}, и используйте консоль для измерения времени выполнения каждого запроса, используя console.time() и console.timeEnd() для вашего запроса или чего-либо еще. Оттуда вы можете увидеть, что мешает вашему коду, используя журналы Cloudwatch. Вот еще один пример, основанный на вашем коде:

let reqOpts = {
  hostname: 'api.servicem8.com',
  port: 443,
  path: '/api_1.0/formresponse.json?%24filter=uuid%20eq%20' + strResponseUUID,
  method: 'GET',
  headers: {
    // Use the temporary Access Token that was issued for this event
    'Authorization': 'Bearer ' + strAccessToken
}
}

console.time("=========MEASURE_servicem8=========")
let error = null;
await request(reqOpts, 5555).catch((e)=>{
  error = e;
})
console.timerEnd("=========MEASURE_servicem8=========")
if (error){
  callback(null, {err: "Unable to query form response records, received HTTP" + error}); /* or anything similar */ 
}

Ссылки
https://docs.aws.amazon.com/lambda/latest/dg/best-practices.html
https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-handler.html

0 голосов
/ 16 июня 2019

aws лямбды не быстрые по своей природе (на момент написания этого ответа).Время запуска не гарантируется, и оно известно как высокое.

Если вам нужна производительность - вы не получите ее таким образом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...