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