跨 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 连接

AWS Lambda 异步并发限制

如何保证提供数据的 aws lambda 调用者保持安全

如何从账户 A 中的 Lambda(VPC 中的 Lambda)调用账户 B 中的 AWS Lambda 函数(VPC 中的这个 Lambda)

AWS beanstalk PrivateLink 未连接

AWS Lambda Snowflake Python 连接器在尝试连接时挂起