如何在lambda函数中正确关闭数据库连接?

21
在我的Lambda函数中,我尝试在发送回调后立即关闭Mongo连接,但出现了问题。
  • 当我发送请求时,函数执行其任务,发送回调并关闭数据库连接。
  • 当发送第二个请求时,函数超时。
  • 当我删除db.close()时,一切正常。

我认为Lambda重用所有函数的连接,因为我在处理程序顶部打开了连接:

// Connect to database
mongoose.connect(process.env.DATABASE_URL);


const handleCreateUser = async (event, context, callback) => {
  // eslint-disable-next-line no-param-reassign
  context.callbackWaitsForEmptyEventLoop = false;

  const data = JSON.parse(event.body);
  const { user, userProfile } = data;

  await createUser({ callback, user, userProfile });
};

有任何修复此问题的想法吗?我们真的必须在此时关闭连接吗?

5个回答

12

要么将 mongoose.connect 的代码放到处理程序内部,要么停止调用 db.close()。目前您的 Lambda 函数有一个数据库连接被多次调用复用,但在第一次调用完成后就关闭了它。


3
是的,我的当前解决方案是删除db.close()。但是这样保持下去可以吗?有什么不利影响吗? - THpubs
4
缺点在于,数据库将不得不在一段超时时间之后自行关闭连接。 - Mark B
1
啊。那么有没有办法看到它是否关闭并重新打开呢?如果连接被数据库关闭,我认为会出现相同的问题。 - THpubs
你的数据库可能不会比Lambda函数容器因为闲置时间而被销毁更快地关闭它,但如果你想添加一个检查,我建议你看看这个链接:https://dev59.com/MGMm5IYBdhLWcg3wMs7R - Mark B
如果将其移动到handleCreateUser函数中,每次Lambda调用后是否会保留一个打开的数据库连接。假设CloudWatch每隔1分钟触发此Lambda函数一次,在1小时后,将会有60个打开的数据库连接吗? - Ujjual
@UJIndia 是的,那是正确的。 - Mark B

3
Lambda会在每次调用中执行你的handleCreateUser函数,但是除了handleCreateUser外的所有内容只会在冷启动时执行。Lambda会缓存这些值以供进一步调用,并且不会再次执行。
mongoose.connect(process.env.DATABASE_URL);

在每次调用中,所以我认为你应该将这段代码移到handleCreateUser函数中。


如果将其移动到handleCreateUser函数中,每次Lambda调用后是否会保留一个打开的数据库连接。假设CloudWatch每隔1分钟触发此Lambda函数一次,那么会有60个打开的数据库连接吗? - Ujjual
如果将其移动到handleCreateUser函数中,它将为每个执行创建新的连接。现在回答您的问题,如果您已在handleCreateUser函数中创建了连接,则可以在函数结束时关闭该连接。这样,在AWS Lambda执行完成后就不会留下任何连接未关闭。 - Dirgh Patel

1
我使用的是Python,但您可以用您喜欢的编程语言。对于lambda函数而言,考虑按时间付费,以下是最佳解决方案: 在lambda函数之前全局运行此代码(我将其放在一个配置类中)。
if self.conn == None or self.conn.close == 1:
  self.make_connection()

如何实现make_connection()由您决定。请不要使用db.close()。

AWS会加载您的Lambda函数并运行全局函数一次。之后,每次调用时,它仅运行保留一段时间(从某些测试中得出的结论是20分钟到50分钟)的Lambda。

连接将在内部超时时由db驱动程序关闭。

优点-您只需在长时间内打开一次连接,为每个Lambda运行节省时间。

缺点-您会一直保持连接,而Lambda在内存中。

我认为这很值得。


0

如果您在处理程序之外创建DB连接,则会在Lambda容器的Init阶段中设置它。然后,它可以被一个或多个调用(Invoke阶段)使用。因此,在处理程序内关闭连接将导致以下调用具有不可用的、已关闭的连接。

为了正确关闭连接(而不是等待超时),您可以使用在容器关闭时触发的钩子。如从这里复制:

// SIGTERM Handler 
process.on('SIGTERM', async () => {
    console.info('[runtime] SIGTERM received');

    console.info('[runtime] cleaning up');
    // perform actual clean up work here. 
    await new Promise(resolve => setTimeout(resolve, 200));
    
    console.info('[runtime] exiting');
    process.exit(0)
});

-5

可以尝试使用try-with-resource,它会自动关闭连接。


1
那并不能解决问题。他已经成功关闭了连接。问题在于他试图在关闭连接后重用它。 - Mark B

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接