Я создаю чат-бота для 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.info("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.info("BODY\n" + JSON.stringify(body));
console.info("<<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.info("<<POSTBACK EVENT>>");
} else {
console.info("UNHANDLED EVENT; " + JSON.stringify(event));
}
})
})
}
}
function callSendAPI(senderPsid, response) {
console.info("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.info("response ::: " + response);
}).catch(function(error) {
console.info(error);
});
}
function storeInMongoDB(messageEnvelope, callback) {
console.info("cachedDB :: " + cachedDb);
if (cachedDb && cachedDb.serverConfig.isConnected()) {
sendToAtlas(cachedDb.db("test"), messageEnvelope, callback);
} else {
console.info(`=> 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.info("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.info(message);
callback(null, message);
}
});
}
Я сделал все в соответствии с инструкциями и сослался на еще несколько подобных случаев, но каким-то образом при каждом запросе значение cachedDb не сохраняется из предыдущего запроса, и приложение снова устанавливает соединение.
Затем я также прочитал, что нет гарантии, что функция Lambda использует один и тот же контейнер для нескольких запросов, поэтому я сделал еще одну глобальную переменную «test». Значение переменной "test" регистрируется как "1" из второго запроса, что означает, что он использует тот же контейнер, но, опять же, значение "cachedDb" не сохраняется.
Что мне здесь не хватает?
Заранее спасибо!
@KMo Я получил "undefined". Но я решил это сейчас. Я думаю, это было потому, что в некоторых случаях, таких как событие «чтение» или «доставка», я не возвращал обратный вызов, поэтому процесс продолжал останавливаться, прежде чем я что-то попробовал.

Короче говоря, функция AWS Lambda не является постоянно работающей службой любого рода.
Итак, я знаю, что AWS Lambda работает по идее - «один контейнер обрабатывает один запрос за раз».
Это означает, что когда приходит запрос и доступен работающий контейнер для функции Lambda, AWS использует его, иначе запускается новый контейнер.
Если второй запрос приходит, когда первый контейнер выполняет функцию Lambda для первого запроса, AWS запускает новый контейнер. и так далее...
Тогда нет гарантии, в каком контейнере (уже запущенном или новом) будет выполняться лямбда-функция, поэтому ... новый контейнер открывает новое соединение с БД.
Конечно, есть период бездействия, и после этого не будет запущенных контейнеров. Все начнется сначала по следующему запросу.
Если вы консольете журнал cachedDb после строки cachedDb = db, что будет выводиться?