AWS Lambda - оптимизация ресурсов MongoDB - PullRequest
0 голосов
/ 11 июня 2018

Я создаю чат-бота в Facebook, используя AWS Lambda и MongoDB.На данный момент мое приложение довольно простое, но я пытаюсь закрепить основы, прежде чем перейти к сложным вещам.

Я понимаю, что AWS Lambda не имеет состояния, но я прочитал добавление строки ниже в обработчике вместе с переменными, инициализированными вне обработчика, мне не нужно устанавливать соединение с БД при каждом запросе.

context.callbackWaitsForEmptyEventLoop = false;

(я читал это из этой статьи; https://www.mongodb.com/blog/post/optimizing-aws-lambda-performance-with-mongodb-atlas-and-nodejs)

Я добавляю весь свой код ниже

'use strict'

const
axios = require('axios'),
mongo = require('mongodb'),
MongoClient = mongo.MongoClient,
assert = require('assert');

var VERIFY_TOKEN = process.env.VERIFY_TOKEN;
var PAGE_ACCESS_TOKEN = process.env.PAGE_ACCESS_TOKEN;
var MONGO_DB_URI = process.env.MONGO_DB_URI;
let cachedDb = null;
let test = null;

exports.handler = (event, context, callback) => {
var method = event.context["http-method"];
context.callbackWaitsForEmptyEventLoop = false;
console.log("test :: " + test);
if (!test) {
    test = "1";
}

// process GET request --> verify facebook webhook
if (method === "GET") {
    var queryParams = event.params.querystring;
    var rVerifyToken = queryParams['hub.verify_token']
    if (rVerifyToken === VERIFY_TOKEN) {
        var challenge = queryParams['hub.challenge'];
        callback(null, parseInt(challenge))
    } else {
        var response = {
            'body': 'Error, wrong validation token',
            'statusCode': 403
        };
        callback(null, response);
    }
    // process POST request --> handle message
} else if (method === "POST") {
    let body = event['body-json'];

    body.entry.map((entry) => {
        entry.messaging.map((event) => {
            if (event.message) {
                if (!event.message.is_echo && event.message.text) {
                    console.log("BODY\n" + JSON.stringify(body));
                    console.log("<<MESSAGE EVENT>>");
                    // retrieve message
                    let response = {
                            "text": "This is from webhook response for \'" + event.message.text + "\'"
                        }
                        // facebook call
                    callSendAPI(event.sender.id, response);

                    // store in DB
                    console.time("dbsave");
                    storeInMongoDB(event, callback);
                }
            } else if (event.postback) {
                console.log("<<POSTBACK EVENT>>");
            } else {
                console.log("UNHANDLED EVENT; " + JSON.stringify(event));
            }

        })
    })
}
}

function callSendAPI(senderPsid, response) {
    console.log("call to FB");
    let payload = {
        recipient: {
            id: senderPsid
        },
        message: response
    };
    let url = `https://graph.facebook.com/v2.6/me/messages?access_token=${PAGE_ACCESS_TOKEN}`;
    axios.post(url, payload)
        .then((response) => {
            console.log("response ::: " + response);
        }).catch(function(error) {
            console.log(error);
        });
}

function storeInMongoDB(messageEnvelope, callback) {
    console.log("cachedDB :: " + cachedDb);
    if (cachedDb && cachedDb.serverConfig.isConnected()) {
        sendToAtlas(cachedDb.db("test"), messageEnvelope, callback);
    } else {
        console.log(`=> connecting to database ${MONGO_DB_URI}`);
        MongoClient.connect(MONGO_DB_URI, function(err, db) {
            assert.equal(null, err);
            cachedDb = db;
            sendToAtlas(db.db("test"), messageEnvelope, callback);
        });
    }
}

function sendToAtlas(db, message, callback) {
    console.log("send to Mongo");
    db.collection("chat_records").insertOne({
        facebook: {
            messageEnvelope: message
        }
    }, function(err, result) {
        if (err != null) {
            console.error("an error occurred in sendToAtlas", err);
            callback(null, JSON.stringify(err));
        } else {
            console.timeEnd("dbsave");
            var message = `Inserted a message into Atlas with id: ${result.insertedId}`;
            console.log(message);
            callback(null, message);
        }
    });
}

Я сделал все, как было указанои ссылался на еще несколько подобных случаев, но каким-то образом при каждом запросе значение «cachedDb» не сохраняется из предыдущего запроса, и приложение снова устанавливает соединение.

Затем я также прочитал, что нет никакой гарантии, чтоЛямбда-функция использует один и тот же контейнер для нескольких запросов, поэтому я создал другую глобальную переменную «test». Значение переменной «test» записывается в «1» из второго запроса, что означает, что он использует тот же контейнер, но опять же, значение «cachedDb» не являетсясохранено.

Что мне здесь не хватает?

Заранее спасибо!

1 Ответ

0 голосов
/ 11 июня 2018

Короче говоря, функция AWS Lambda не является постоянно работающей службой любого типа.

Итак, насколько я знаю, AWS Lambda работает над идеей - «один контейнер обрабатывает один запрос за раз».

Это означает, что при поступлении запроса и наличии доступного работающего контейнера для лямбда-функции AWS использует его, иначе он запускает новый контейнер.

Если второй запрос приходит, когда первый контейнер выполняет лямбда-функцию для первого запроса, AWS запускает новый контейнер.и так далее ...

Тогда нет гарантии, в каком контейнере (уже запущенном или новом) будет выполняться лямбда-функция, поэтому ... новый контейнер открывает новое соединение с БД.

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

...