我试图了解以下是好的还是坏的做法。
如果事实证明这是正常的做法,那么我就可以更轻松地在项目中实现多个数据库连接器。
情况:我已经创建了一个API服务器,每次调用API时,这段代码就会运行:
ctx := context.Background()
client, err := datastore.NewClient(ctx, "foobar")
if err != nil {
log.Fatalf("Failed to create client: %v", err)
}
该服务每秒收到很多请求,我不确定是否应该在启动时或每次调用API时运行newClient()
函数。
PS:
如果另一个连接器是MySQL,则会发生同样的情况。每次API获取请求时,都会运行以下代码:
db, err = sql.Open("mysql", "yourusername:yourpassword@/yourdatabase")
if err != nil {
panic(err.Error())
}
答案 0 :(得分:2)
我不确定datastore.Client
,但是database.DB
管理一个连接池,对于并发使用是安全的,每次你想要的时候都不应该真正调用db.Open
执行查询。实际上,Open's文档的最后一段清楚地表明了这一点。
返回的数据库对于多个goroutine和并发使用是安全的 维护自己的空闲连接池。因此,Open功能 应该只召唤一次。很少需要关闭数据库。
<强>更新强>:
如果您查看datastore
文档中Basic Operations部分示例代码中的注释,您会找到推荐用途。
创建数据存储客户端。在典型的应用程序中,您会 创建一个可以为每个数据存储重用的客户端 操作
答案 1 :(得分:0)
我没有使用Google数据存储区的任何经验,但我希望它的设计与database/sql
包类似。在这种情况下,在每个请求上使用sql.Open
并不是很糟糕,因为连接被懒惰地初始化并且它没有验证参数。但重复使用它会好得多,因为它可以安全地同时使用。
虽然在完成数据库时Close()数据库是惯用的,但sql.DB对象的设计是长期存在的。不经常打开()和关闭()数据库。而是为您需要访问的每个不同数据存储创建一个sql.DB对象,并保留它直到程序完成访问该数据存储。根据需要传递它,或者以某种方式在全球范围内提供它,但保持打开状态。并且不要从短期函数中打开()和关闭()。相反,将sql.DB作为参数传递给该短期函数。
如果不将sql.DB视为长期存在的对象,则可能会遇到诸如重用不良和连接共享,可用网络资源不足或由于大量TCP连接导致的偶发故障等问题保持TIME_WAIT状态。这些问题表明你没有像设计那样使用数据库/ sql。
来自:http://go-database-sql.org/accessing.html
默认情况下,它由连接池支持,没有连接限制。因此,您可能会使用足够的并发goroutine使其饿死(更不用说某些数据库系统具有非常繁重的连接初始化)。但是,您可以非常轻松地限制池大小:http://go-database-sql.org/connection-pool.html
答案 2 :(得分:0)
这是一个基于偏好的答案,但这是为了它的价值。
使用包github.com/mjibson/goon
满足您的所有datastore
需求。它很好地包含了Get
和Put
之类的调用,并且会为你制定Marshal / Unmarshal structs
。它还会自动通过memcache
运行任何关键查询,以加快速度。
回到你的实际问题。 goon
适合在每次请求时运行这两行中的一行。
g := goon.FromContext(c) // if you have Context
g := goon.NewGoon(req) // if you have http.Request