2

所以我按照这里的例子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控制台日志都不会运行。

为什么这不起作用?这是因为无服务器离线吗?

4

1 回答 1

2

答案很简单:serverless-offline不模拟完整的 AWS。使用 AWS 控制台制作真正的 Lambda

MongoDB Atlas 指南还可以,但也值得查看官方AWS Lambda 文档,其中描述了context每个 lambda 中的选项:

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
}

使用测试按钮在 AWS Lambda 控制台中运行上述 lambda两次。

  • 第一次运行你会看到Connecting to database (cold start)

  • 第二次你会看到Already connected to database, warm start!

请参阅下面屏幕截图中的日志输出部分:

在此处输入图像描述

于 2019-04-08T09:03:25.350 回答