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

时间:2019-01-09 12:57:13

标签: go aws-lambda connection-pooling

据我在Golang中了解:the DB handle is meant to be long-lived and shared between many goroutines

但是当我将Golang与AWS lambda结合使用时,情况就大不相同了,因为lambda在完成后会停止该功能。

我正在Lambda调用函数中使用:defer db.Close(),但不受影响。在MySQL上,它仍然保持该连接为Sleep query。结果,它在MySQL上导致too many connections

当前,我必须将MySQL中的wait_timeout设置为小数。但我认为这不是最佳解决方案。

在Lambda上使用Go SQL驱动程序时,是否可以关闭连接?

谢谢

1 个答案:

答案 0 :(得分:3)

我们需要解决两个问题

  • 在lambda调用之间正确管理状态
  • 配置连接池

正确管理状态

让我们了解AWS如何管理容器。来自AWS docs

  

执行Lambda函数后,AWS Lambda会维护   在预期另一个Lambda的情况下执行上下文   函数调用。实际上,该服务会冻结执行   Lambda函数完成后的上下文,并解冻   重用(如果AWS Lambda选择在Lambda时重用上下文)   函数再次被调用。   这种执行上下文重用方法具有以下含义:

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

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

  •   
  • Lambda函数启动的后台进程或回调   如果AWS 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错误。如果发生这种情况,您将需要一个连接池。

配置连接池

来自Wikipedia

  

在软件工程中,连接池是数据库的缓存   保持连接,以便在以下情况下可以重用连接   以后需要对数据库的请求。连接池是   用于提高在数据库上执行命令的性能。

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

  • MySQL代理
  

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

  • AWS Aurora:
  

Amazon Aurora Serverless是按需自动缩放配置   适用于Amazon Aurora(与MySQL兼容的版本),数据库将在其中   自动启动,关闭并根据容量增加或减少容量   根据您的应用程序的需求。它使您可以在以下位置运行数据库   云,无需管理任何数据库实例。很简单   具有成本效益的选项,可用于偶尔,间歇或不可预测的情况   工作量。

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