Использование библиотеки базы данных/sql и получение пароля из хранилища при сбое нового подключения

У меня есть давно работающий демон, написанный на Go, который прослушивает порт и запускает несколько процедур go для каждого нового подключения для обработки данных. Существует глобальная переменная db, которой назначается контекст соединения, возвращаемый функцией database/sql библиотеки open() в функции main() моего скрипта.

Мы храним пароль базы данных в хранилище, которое меняет его каждые пару дней из соображений безопасности. Я могу получить пароль из хранилища при первом создании контекста подключения, и этот же контекст используется во всех процедурах go для создания новых подключений к БД. Однако, когда пароль меняется хранилищем, все новые подключения к базе данных терпят неудачу. Я хотел бы знать, как лучше всего справиться с этим, чтобы он извлекал пароль из хранилища при сбое и повторно подключался. Если бы это был язык oop, я мог бы расширить библиотеку db и переопределить функцию подключения, чтобы перехватить ошибку и получить пароль из хранилища при сбое подключения. Есть ли аналогичный подход, который можно использовать в Go, или есть ли другой способ справиться с этим? Я очень новичок в Go, и прошу прощения, если я не правильно сформулировал вопрос.

package main

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
    "net"
)

var db *sql.DB
const port = "port number"

func main() {

    db, err = sql.Open("mysql","<Connection string that contains the password fetched from vault>")

    db.SetMaxOpenConns(100)

    listener, err := net.Listen("tcp", ":"+port)

    for {
        conn, err := listener.Accept() 

        go handleConnection(conn)
    }
}

func handleConnection(conn net.Conn) {
    // Uses db variable to connect to db.
}

Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
API ввода вопросов - это полезный инструмент для интеграции моделей машинного обучения, таких как ChatGPT, в приложения, требующие обработки...
0
0
653
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Зачем вам создавать контекст на main? почему бы не создать отдельный пакет/пространство имен и выполнить там подключения к базе данных. создайте новое пространство имен с методами... createDBClient() создает новые контексты базы данных, извлекая пароль из хранилища и сохраняя его в глобальной переменной db. GetDBClient () проверяет, есть ли что-то в глобальной переменной db, если да, проверьте, может ли она подключиться к db (https://golang.org/pkg/database/sql/#DB.Ping) если не вызвать цикл createDBClient(), это несколько раз со сном, если не подключается сбой и запись если он соединяется, верните контекст

теперь в вашем handleConnection() вызовите db := newnamespace.GetDBClient()

также причина, по которой я сказал создать другой пакет/пространство имен, заключается в том, что если вы собираетесь сделать var db глобальным, то все, что находится в main, может получить к нему доступ и изменить, если оно находится в другом пространстве имен, а переменная или метод, начинающийся с нижнего регистра, становится частным для этого пространства имен, так намного безопаснее.

Abhishek Salian 04.06.2019 03:39
sql.Open не создает соединение. Он только создает контекст. Соединение создается при выполнении запроса, что означает в handleConnection(). Я могу поймать ошибку, а затем повторно подключиться, но я не хочу ждать, пока запрос не сможет повторно подключиться с новым паролем, а вместо этого поймать ошибку неправильного подключения, а затем создать новое подключение, извлекая пароль из хранилища. Однако я не уверен, как я могу поймать ошибку плохого соединения, поскольку библиотека не выдает ее (может быть, я ошибаюсь).
Rohith Mohan 04.06.2019 03:44

да правда, я хотел сказать контекст. обновленный ответ с конкретными деталями

Abhishek Salian 04.06.2019 04:07

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

Rohith Mohan 04.06.2019 04:23

не при каждом sql-запросе, непосредственно перед тем, как вы начнете выполнять базовые запросы внутри вашего handleConnection(), поэтому каждый раз, когда вы выполняете этот метод.

Abhishek Salian 04.06.2019 04:26

Это долго работающий демон, и этот метод запускается всякий раз, когда устанавливается новое соединение tcp, что происходит почти каждый раз.

Rohith Mohan 04.06.2019 04:28

«Все, что я хотел сделать, это когда библиотека обнаружит ошибку плохого соединения и создаст новое соединение, заставить ее использовать новый пароль, который может решить проблему». тогда вам нужно дождаться сбоя запроса, а затем повторить попытку. либо проверьте перед тем, что безопаснее. ping только проверяет состояние соединения, не выполняет никаких запросов, или вам нужно дождаться его сбоя. я предпочитаю пинг сбою.

Abhishek Salian 04.06.2019 04:28

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

Rohith Mohan 04.06.2019 04:36
Ответ принят как подходящий

Я решил эту проблему, создав собственный драйвер, который импортирует github.com/go-sql-driver/mysql и регистрируется в пакете sql, так что, когда требуется новое соединение, пользовательский драйвер может получить пароль из хранилища и передать это до драйвера mysql, чтобы открыть соединение. См. пример кода ниже.

package vault-mysql-driver

import (
    "database/sql"
    "github.com/go-sql-driver/mysql"
)

type VaultMysqlDriver struct {
    *mysql.MySQLDriver
}

func updateDsn(dsn string) (string, err) {
    // logic to fetch password from vault and update dsn with the password
}

func (d VaultMysqlDriver) Open(dsn string) (driver.Conn, error) {

    updateddsn, err := updateDsn(dsn)

    // Pass down the dsn with password to mysql driver's open function
    return d.MySQLDriver.Open(updateddsn)

}

// When initialised will register the driver in sql package
func init() {
    sql.Register(vault-driver, &CyberarkMysqlDriver{&mysql.MySQLDriver{}})
}

Этот пакет теперь импортируется в демон, как показано ниже:

import (
    "database/sql"
    _ "vault-mysql-driver"// init is invoked and it will get registered in sql package
    "net"
)

var db *sql.DB
const port = "port number"

func main() {
    // vault-driver is used instead of mysql so that the sql package knows to use the custom driver for new connections.
    db, err = sql.Open("vault-driver","<Connection string that contains the password fetched from vault>")

    db.SetMaxOpenConns(100)

    listener, err := net.Listen("tcp", ":"+port)

    for {
        conn, err := listener.Accept() 

        go handleConnection(conn)
    }
}

func handleConnection(conn net.Conn) {
    // Uses db variable to connect to db.
}

Таким образом, всякий раз, когда хранилище меняет пароль, не будет сбоя соединения, поскольку драйвер хранилища всегда будет извлекать пароль из хранилища для каждого нового соединения.

Другие вопросы по теме