在Golang中与多个包共享全局定义的db conn

use*_*811 36 go

我已经阅读了一些关于如何处理数据库连接的StackOverflow答案.由于它是一个池,我们可以在全局定义它并在多个goroutine中使用它并且它是安全的.

我遇到的问题是我已将REST API拆分为多个包.这些包中的每一个都需要数据库连接,因此我在启动时打开数据库连接.但即使我全局定义连接,它也只是在包级别.我可以做些什么来在多个包中分享它?

对于某些上下文,我在我的应用程序中使用PostgreSQL驱动程序和gin-gonic.

Maa*_*aaz 77

还可以选择创建另一个包来保存与数据库连接相关的设置.然后它可以具有全局包级别,可以main在任何导入它的包中初始化并使用它.

这样,您可以明确地看到正在导入数据库包.这是一些示例代码.

package database

var (
    // DBCon is the connection handle
    // for the database
    DBCon *sql.DB
)
Run Code Online (Sandbox Code Playgroud)
package main

import "myApp/database"

func main() {

    var err error
    database.DBCon, err = sql.Open("postgres", "user=myname dbname=dbname sslmode=disable")

}
Run Code Online (Sandbox Code Playgroud)
package user

import "myApp/database"

func Index() {
    // database handle is available here
    database.DBCon

    ...
}
Run Code Online (Sandbox Code Playgroud)

  • @gpanda我想这取决于,我将其视为长期的联系。例如,如果您使用 gin-gonic 或其他东西,您可以检测服务器是否收到关闭信号,然后关闭数据库连接,例如:https://github.com/gin-gonic/gin/blob/主/示例/graceful-shutdown/graceful-shutdown/server.go (2认同)
  • 我们可以使用单例模式吗? (2认同)

eli*_*rar 11

简单的答案:将初始化的连接池传递给包的自己的全局变量.

例如

// package stuff

var DB *sql.DB

func GetAllStuff() (*Stuff, error) {
    err := DB.Query("...")
    // etc.
}

// package things

var DB *sql.DB

func GetAllThings() (*Thing, error) {
    err := DB.Query("...")
    // etc.
}

// package main

func main() {
    db, err := sql.Open("...")
    if err != nil {
        log.Fatal(err)
    }

    stuff.DB = db
    things.DB = db

    // etc.
}
Run Code Online (Sandbox Code Playgroud)

我们定义包级别的全局变量,确保它们被导出(大写),然后将指针传递给它们的连接池.

这是"好的",但可以屏蔽"使用"的东西.如果你正在寻找一个处理程序可能并不清楚其中的连接是从哪里来的,尤其是你的包增长.更具伸缩性的方法可能如下所示:

// package stuff

type DB struct {
    *sql.DB
}

func New(db *sql.DB) (*DB, error) {
    // Configure any package-level settings
    return &DB{db}, nil
}

func (db *DB) GetAllStuff() (*Stuff, error) {
    err := db.Query("...")
    // etc.
}

// package things

type DB struct {
    *sql.DB
}

func New(db *sql.DB) (*DB, error) {
    // Configure any package-level settings
    return &DB{db}, nil
}

func (db *DB) GetAllThings() (*Thing, error) {
    err := db.Query("...")
    // etc.
}

// package main

func main() {
    db, err := sql.Open("...")
    if err != nil {
        log.Fatal(err)
    }

    stuffDB, err := stuff.New(db)
    if err != nil {
        log.Fatal(err)
    }

    thingsDB, err := things.New(db)
    if err != nil {
        log.Fatal(err)
    }

    // Simplified.
    http.HandleFunc("/stuff/all", stuff.ShowStuffHandler(stuffDB))
    http.HandleFunc("/things/all", things.ShowThingsHandler(thingsDB))

    // etc.
}

func ShowStuffHandler(db *stuff.DB) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        // We can use our stuff.DB here
        stuff, err := db.GetAllStuff()
        // etc.
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您不仅仅将数据库连接作为依赖项(例如config params,hostnames等),请将它们包装在每个包的things.Envstruct或stuff.Envstruct中.

一个例子是有一个things.New("deps...") *Env函数返回一个*things.Env包含你的things包使用的依赖项的配置.

  • PS:我写了一篇关于如何使用处理程序或包依赖关系传递结构的文章:http://elithrar.github.io/article/http-handler-error-handling-revisited/(对旧文章的更新)可能会帮助你从另一个角度看待它.另请参阅http://www.jerf.org/iri/post/2929和https://medium.com/@benbjohnson/structuring-applications-in-go-3b04be4ff091,这些都是这些方法的补充. (2认同)