Go 如何在多个包之间共享单个数据库连接

Go 如何在多个包之间共享单个数据库连接,go,Go,我有两个名为client和worker的包。我想与这两个包共享相同的ssdb、mysql和redis连接。 我在这两个包之间共享auth时面临的另一个类似问题 app -> client pkg -> worker pkg main.go (contains auth as global variable) 有谁能给我建议一下实现这两件事的最佳方法吗?有很多方法可以做到这一点,每种方法都有优缺点,这取决于你在做什么。一个简单的方法是使用第三个包和DB连接,并从其他两个包

我有两个名为client和worker的包。我想与这两个包共享相同的ssdb、mysql和redis连接。 我在这两个包之间共享auth时面临的另一个类似问题

app
  -> client pkg
  -> worker pkg
  main.go (contains auth as global variable)

有谁能给我建议一下实现这两件事的最佳方法吗?

有很多方法可以做到这一点,每种方法都有优缺点,这取决于你在做什么。一个简单的方法是使用第三个包和DB连接,并从其他两个包和主包导入它

app
  -> client pkg // import "app/global"
  -> worker pkg // import "app/global"
  -> global pkg // Contains ssdb and auth as global variables
  main.go
另一种可能更好的方法是让
客户端
工作者
包上的函数接受db连接作为参数,并从
main.go
初始化db,并在调用需要它的函数时将其作为参数传递

这取决于您正在做什么,但是对于大型项目,如果您只需要一个包来执行所有的db操作,并且您可以从所有需要执行操作的地方访问它,那么维护起来就更容易了。这样您就不必担心这个问题,因为即使有几个包使用db连接,也只有一个包需要担心db连接

编辑:

全局变量的问题是,它们可以同时从项目中的任何地方进行修改,并且可以引入竞争条件,但是在没有问题的情况下使用它们并没有错

在这种情况下,您只需在连接到DB并使用对象时设置一次值


您提到您希望为auth提供另一个包,我建议只使用一个包,并在其中包含从多个包(在本例中为ssdb和auth)访问所需的所有内容。

对于新的Go开发人员来说,有一种方法并不总是显而易见的,这是一种很难实现但并不可怕的方法 复杂,通常在初学者应用程序中运行良好:

app
    client // imports storage
    worker // imports storage
    config // all environment-related config goes here
    storage   // storage-engine-generic interface to the packages below it
        ssdb  // all ssdb-specific code here
        mysql // all mysql-specific code here
        redis // ditto
它使用包变量。如果您担心意外写入导出的包变量,可以通过使用未导出的包变量来避免该问题。利用 Go中导出标识符的有限定义(请参阅)

main
中,调用

config.Init(configfile)
storage.Init()
定义
config.Init
函数以读取配置文件并将包变量设置为您的连接信息 数据库。如果您使用的是eExported包变量,则允许通过导出函数进行公共只读访问。否则,您可能可以跳过这些功能,具体取决于您需要的其他功能

storage
中,您的
Init
函数调用

ssdb.Init()
mysql.Init()
redis.Init()
然后,在
存储
中,您将拥有
客户端
服务器
使用的非特定于存储引擎的公共功能,例如

func GetImage(id string) ([]byte) {
    return mysql.GetImage(id)
}
或者任何适合你的申请的。
存储
抽象级别可能值得,也可能不值得,这取决于您将来如何更改应用程序。你决定是否值得投资

mysql
包中,您导入
config
,您有

var db *sql.DB
func Init() {
    getDb()
}
func getDb() (*sql.DB) {
    if db == nil {  // or something
        config.Log.Println("Opening db connection to mysql")
        db, err := sql.Open("mysql", config.MysqlConnectionString())
        // do something with err, possibly have a retry loop
    }
    return db
}
func GetImage(id string) ([]byte)
     db := getDb()
     // ...
mysql包中的函数可以使用
db
unexported包变量,但其他包不能

使用带有导出函数只读访问的未导出包变量不是一种可怕的做法,也不是特别复杂。也就是说,这通常是不必要的。如果
db
是导出的包变量
db
,您会突然键入

mysql.Db, _ = sql.Open("mysql", "LEEERRROOYYYYY!!!!")
在您的
客户机中
代码(并决定导入
mysql
sql
来完成),然后部署到生产环境中?为什么您更可能这样做,而不是故意破坏代码的任何其他部分

请注意,如果您只是键入

mysql.Db = "LEEERRROOYYYYYY!!!!"

由于类型不匹配,您的应用程序将无法编译。

您尝试过哪些方法?您遇到了什么问题,或者您预期会遇到什么问题?我为db制作了一个单独的包,并为连接池提供了一个全局变量。然后将其导入两个包中。但这是正确的处理方式吗?如果我以后再遇到同样的情况,还有一件事。。每次我都必须添加一个新包?创建一个包含配置的全局包,并将其导入到您的包中。我希望一起创建一个不同的包,并在两个包中导入。但问题是,我读到保持全局变量不是一个好的做法。还有一件事就是db。。同样的事情也需要auth,因为我的两个包都需要auth。同样,我必须为auth创建一个新的包,并将auth作为一个全局变量。这种方法将在这个项目中产生如此多的包。@NidhiAgarwal请查看我答案的编辑。另外,auth是什么类型的变量,您如何使用它?如果你从不同的goroutine写文章,你需要确保它的线程安全。谢谢你的帮助。我还有一个疑问。因此,我将所有常用的包移动到一个全局包。但例如,我从sql包(global->sql->sql.go)导出conn,并在其他包中使用它。我可能还需要一些来自实际sql包的结构类型来定义变量。如何从全局导出它们?@NidhiAgarwal如果直接从sql包中使用这些结构,并将DB连接保留在全局中,我不认为这是错误的,但是如果您想将它们都放在同一个包中,则需要在全局包中创建一个新的导出类型,然后在需要它们的函数中使用它们时,将它们转换为sql类型。您可以通过使用sql结构作为基础创建一个新类型来实现这一点:
键入MyStruct sql.StructName
并阅读此内容以了解类型转换:
键入MyStruct sql.StructName
jeeennkiinnssss