sql.DB на AWS-лямбда слишком много соединения - PullRequest
0 голосов
/ 09 января 2019

Как я понимаю на Голанге: the DB handle is meant to be long-lived and shared between many goroutines.

Но когда я использую Golang с лямбдой AWS, это совсем другая история, поскольку лямбды останавливают функцию, когда она заканчивается.

Я использую: defer db.Close() в функции Lambda Invoke, но это не влияет. В MySQL это соединение по-прежнему сохраняется как Sleep query. В результате это вызывает too many connections на MySQL.

В настоящее время я должен установить wait_timeout в MySQL на небольшое число. Но это не лучшее решение, на мой взгляд.

Есть ли способ закрыть соединение при использовании драйвера Go SQL с Lambda?

Спасибо

1 Ответ

0 голосов
/ 09 января 2019

Есть две проблемы, которые нам нужно решить

  • Правильное управление состоянием между лямбда-вызовами
  • Настройка пула соединений

Правильно управляющее состояние

Давайте немного разберемся, как контейнер управляется AWS. Из документов AWS :

После выполнения лямбда-функции AWS Lambda поддерживает контекст выполнения в течение некоторого времени в ожидании другой лямбды вызов функции. По сути, сервис останавливает выполнение контекст после завершения лямбда-функции и оттаивания контекста для повторное использование, если AWS Lambda выбирает повторное использование контекста, когда Lambda функция вызывается снова. Этот подход повторного использования контекста выполнения имеет следующие последствия:

  • Любые объявления в вашем коде функции Lambda (вне обработчика код, см. Модель программирования) остается инициализированным, обеспечивая дополнительные оптимизация, когда функция вызывается снова. Например, если ваш Лямбда-функция устанавливает соединение с базой данных, а не восстановление соединения, исходное соединение используется в последующие вызовы. Мы рекомендуем добавить логику в ваш код для проверки если соединение существует до его создания.

  • Каждый контекст выполнения предоставляет 500 МБ дополнительного дискового пространства в Каталог / tmp. Содержимое каталога остается при выполнении контекст заморожен, предоставляя временный кеш, который можно использовать для несколько вызовов. Вы можете добавить дополнительный код, чтобы проверить, имеет ли кеш данные, которые вы сохранили. Для получения информации об ограничениях развертывания см. AWS Lambda Limits.

  • Фоновые процессы или обратные вызовы, инициированные вашей функцией 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. Если это произойдет, вам понадобится пул соединений.

Настройка пула соединений

Из Википедия :

В программной инженерии пул соединений - это кеш базы данных. Соединения поддерживаются, так что соединения могут быть использованы повторно, когда Будущие запросы к базе данных не требуется. Пулы соединений используется для повышения производительности выполнения команд в базе данных.

Вам потребуется пул соединений, в котором количество разрешенных соединений должно быть равно количеству запущенных параллельных лямбд, у вас есть два варианта:

  • MySQL Proxy

MySQL Proxy - это простая программа, которая находится между вашим клиентом и MySQL сервер (ы) и которые могут контролировать, анализировать или преобразовывать их коммуникации. Его гибкость допускает широкий спектр применения, включая балансировку нагрузки, отработку отказа, анализ запросов, фильтрацию запросов и модификации, и многое другое.

  • AWS Aurora:

Amazon Aurora Serverless - конфигурация с автоматическим масштабированием по требованию для Amazon Aurora (MySQL-совместимое издание), где база данных будетавтоматический запуск, выключение и масштабирование емкости в зависимости от увеличения или уменьшения на потребности вашего приложения. Это позволяет вам запустить вашу базу данных в облако без управления экземплярами базы данных. Это просто, экономически эффективный вариант для редких, периодических или непредсказуемых рабочие нагрузки.

Независимо от вашего выбора, в интернете есть множество руководств по настройке обоих.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...