为了账号安全,请及时绑定邮箱和手机立即绑定

aws-lambda 上的 sql.DB 连接太多

aws-lambda 上的 sql.DB 连接太多

Go
吃鸡游戏 2023-06-01 14:14:37
正如我在 Golang 中的理解:the DB handle is meant to be long-lived and shared between many goroutines.但是当我将 Golang 与 AWS lambda 结合使用时,情况就完全不同了,因为 lambda 会在函数完成时停止该函数。我正在使用:defer db.Close()在 Lambda Invoke 函数中,但它没有影响。在 MySQL 上,它仍将连接保持为Sleep query. 结果,它导致too many connections了 MySQL。目前,我必须wait_timeout在 MySQL 中设置为小数。但在我看来,这不是最好的解决方案。在 Lambda 中使用 Go SQL 驱动程序时,有什么方法可以关闭连接吗?谢谢,
查看完整描述

1 回答

?
慕标5832272

TA贡献1966条经验 获得超4个赞

我们需要解决两个问题

  • 正确管理 lambda 调用之间的状态

  • 配置连接池

正确管理状态

让我们稍微了解一下 AWS 是如何管理容器的。来自AWS 文档:

执行 Lambda 函数后,AWS Lambda 会在一段时间内维护执行上下文,以等待另一个 Lambda 函数调用。实际上,如果 AWS Lambda 选择在再次调用 Lambda 函数时重用上下文,服务会在 Lambda 函数完成后冻结执行上下文,并解冻上下文以供重用。这种执行上下文重用方法具有以下含义:

  • Lambda 函数代码中的任何声明(在处理程序代码之外,请参阅编程模型)保持初始化状态,从而在再次调用该函数时提供额外的优化。例如,如果您的 Lambda 函数建立数据库连接,而不是重新建立连接,则在后续调用中使用原始连接。我们建议在您的代码中添加逻辑以在创建连接之前检查连接是否存在。

  • 每个执行上下文在 /tmp 目录中提供 500MB 的额外磁盘空间。当执行上下文被冻结时,目录内容仍然存在,提供可用于多次调用的临时缓存。您可以添加额外的代码来检查缓存是否包含您存储的数据。有关部署限制的信息,请参阅 AWS Lambda 限制。

  • 如果 AWS Lambda 选择重用执行上下文,则由您的 Lambda 函数启动但在函数结束时未完成的后台进程或回调将恢复。您应该确保代码中的任何后台进程或回调(在 Node.js 的情况下)在代码退出之前完成。

第一个要点表示状态在执行之间保持不变。让我们看看实际效果:

let counter = 0
module.exports.handler = (event, context, callback) => {
  counter++
  callback(null, { count: counter })
}

如果您部署它并连续多次调用,您将看到计数器将在两次调用之间递增。

现在您知道了 - 您不应该调用defer db.Close(),而应该重用数据库实例。您可以通过简单地创建db一个包级变量来做到这一点。

首先,创建一个将导出Open函数的数据库包:

package database


import (

    "fmt"

    "os"


    _ "github.com/go-sql-driver/mysql"

    "github.com/jinzhu/gorm"

)


var (

    host = os.Getenv("DB_HOST")

    port = os.Getenv("DB_PORT")

    user = os.Getenv("DB_USER")

    name = os.Getenv("DB_NAME")

    pass = os.Getenv("DB_PASS")

)


func Open() (db *gorm.DB) {

    args := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?parseTime=true", user, pass, host, port, name)

    // Initialize a new db connection.

    db, err := gorm.Open("mysql", args)

    if err != nil {

        panic(err)

    }

    return

}

然后在你的 handler.go 文件中使用它:


package main


import (

    "context"


    "github.com/aws/aws-lambda-go/events"

    "github.com/aws/aws-lambda-go/lambda"

    "github.com/jinzhu/gorm"

    "github.com/<username>/<name-of-lib>/database"

)


var db *gorm.DB


func init() {

    db = database.Open()

}


func Handler() (events.APIGatewayProxyResponse, error) {

    // You can use db here.

    return events.APIGatewayProxyResponse{

        StatusCode: 201,

    }, nil

}


func main() {

    lambda.Start(Handler)

}

OBS:不要忘记替换github.com/<username>/<name-of-lib>/database为正确的路径。

现在,您可能仍然会看到too many connections错误。如果发生这种情况,您将需要一个连接池。

配置连接池

在软件工程中,连接池是维护的数据库连接的缓存,以便在以后需要对数据库的请求时可以重用这些连接。连接池用于增强在数据库上执行命令的性能。

您将需要一个连接池,允许的连接数必须等于运行的并行 lambda 数,您有两种选择:

  • MySQL 代理

MySQL Proxy 是一个简单的程序,位于您的客户端和 MySQL 服务器之间,可以监视、分析或转换它们的通信。它的灵活性允许广泛的用途,包括负载平衡、故障转移、查询分析、查询过滤和修改等等。

  • AWS 极光:

Amazon Aurora Serverless 是 Amazon Aurora(MySQL 兼容版本)的按需自动扩展配置,其中数据库将根据您的应用程序的需要自动启动、关闭和扩展或缩减容量。它使您能够在云中运行数据库而无需管理任何数据库实例。对于不频繁、间歇性或不可预测的工作负载,这是一种简单、经济高效的选择。

无论您选择哪种方式,互联网上都有大量关于如何配置两者的教程。


查看完整回答
反对 回复 2023-06-01
  • 1 回答
  • 0 关注
  • 103 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信