跨 AWS Lambda 函数调用共享数据库连接
Posted
技术标签:
【中文标题】跨 AWS Lambda 函数调用共享数据库连接【英文标题】:Sharing DB Connection across AWS Lambda function calls 【发布时间】:2018-01-24 03:17:15 【问题描述】:所以我按照https://www.mongodb.com/blog/post/optimizing-aws-lambda-performance-with-mongodb-atlas-and-nodejs 的示例来优化我的 lambda 函数。
我尝试了两种方法,并在本地使用 serverless-offline
对其进行了测试,但似乎都不起作用。
第一种方法
// endpoint file
import connectToDatabase from "lib/dbUtils.js";
let cachedDb = null;
export function post(event, context, callback)
let response;
context.callbackWaitsForEmptyEventLoop = false;
connectToDatabase()
.then(//do other stuff
// lib/dbUtils.js
export async function connectToDatabase()
if (cachedDb && cachedDb.serverConfig.isConnected())
console.log(" using cached db instance");
return cachedDb;
cachedDb = await mongoose.createConnection(
process.env.DB_URL,
async err =>
if (err)
throw err;
);
return cachedDb;
第二种方法
global.cachedDb = null;
export function post(event, context, callback)
let response;
context.callbackWaitsForEmptyEventLoop = false;
connectToDatabase()
.then(connection => createUser(event.body, connection))
// lib/dbUtils.js
export async function connectToDatabase()
// eslint-disable-next-line
if (global.cachedDb && global.cachedDb.serverConfig.isConnected())
// eslint-disable-next-line
console.log(" using cached db instance");
// eslint-disable-next-line
return global.cachedDb;
// eslint-disable-next-line
global.cachedDb = await mongoose.createConnection(
process.env.DB_URL,
async err =>
if (err)
throw err;
);
// eslint-disable-next-line
return global.cachedDb;
在这两种情况下,using cached db instance
控制台日志都不会运行。
为什么这不起作用?这是因为 serverless-offline 的原因吗?
【问题讨论】:
先在AWS上试用,再评论 您正在尝试的是完全合法的——不能保证容器会被重用,但很有可能会被重用。我不明白为什么你似乎让这个测试用例变得比它需要的复杂得多。这可能是范围界定问题吗?将所有这些代码放在一个文件中,使其工作,然后将其拆分为不同的文件。您正在测试两个条件if (cachedDb && cachedDb.serverConfig.isConnected())
但您没有记录第一个条件以查看它是否为真。
【参考方案1】:
答案很简单:serverless-offline
不模拟完整的 AWS。 使用 AWS 控制台制作真正的 Lambda
MongoDB Atlas guide 没问题,但也值得检查官方 AWS Lambda documentation 描述每个 lambda 中的 context
选项:
callbackWaitsForEmptyEventLoop
– 设置为 false 以在回调执行时立即发送响应,而不是等待 Node.js 事件循环为空。如果为 false,则任何未完成的事件将在下一次调用期间继续运行。
可以在真正的 Lambda 上运行您的代码并在控制台上查看 using cached db instance
。由于 MongoDB 的 javascript 代码相当糟糕,我在下面写出了自己的版本:
var MongoClient = require("mongodb").MongoClient
let db = null
var log = console.log.bind(console)
var print = function(object)
return JSON.stringify(object, null, 2)
// Use your own credentials (and better yet, put them in environment variables)
const password = `notactuallyapassword`
const uri = `mongodb+srv://lambdauser:$password@fakedomain.mongodb.net/test?retryWrites=true`
exports.handler = function(event, context, callback)
log(`Calling MongoDB Atlas from AWS Lambda with event: $print(event)`)
var document = JSON.parse(JSON.stringify(event))
const databaseName = "myDatabase",
collectionName = "documents"
// See https://www.mongodb.com/blog/post/optimizing-aws-lambda-performance-with-mongodb-atlas-and-nodejs
// and https://docs.aws.amazon.com/lambda/latest/dg/nodejs-prog-model-context.html#nodejs-prog-model-context-properties
context.callbackWaitsForEmptyEventLoop = false
return createDoc(databaseName, collectionName, document)
async function createDoc(databaseName, collectionName, document)
var isConnected = db && db.serverConfig.isConnected()
if (isConnected)
log(`Already connected to database, warm start!`)
else
log(`Connecting to database (cold start)`)
var client = await MongoClient.connect(uri)
db = client.db(databaseName)
var result = await db.collection(collectionName).insertOne(document)
log(`just created an entry into the $collectionName collection with id: $result.insertedId`)
// Don't close the connection thanks to context.callbackWaitsForEmptyEventLoop = false - this will re-use the connection on the next called (if it can re-use the same Lambda container)
return result
使用 Test 按钮在 AWS Lambda 控制台中运行上述 lambda 两次。
第一次运行时,您会看到Connecting to database (cold start)
你会第二次看到Already connected to database, warm start!
请参阅下面屏幕截图中的日志输出部分:
【讨论】:
以上是关于跨 AWS Lambda 函数调用共享数据库连接的主要内容,如果未能解决你的问题,请参考以下文章
在 AWS Lambda 上处理未初始化或错误的 Redis 连接
如何从账户 A 中的 Lambda(VPC 中的 Lambda)调用账户 B 中的 AWS Lambda 函数(VPC 中的这个 Lambda)