英文:
Concurrently using the same mgo session in go
问题
所以我在使用Go语言与MongoDB并发时遇到了一些问题。我最初的获取会话的实现如下:
var globalSession *mgo.Session
func getSession() (*mgo.Session, error) {
// 建立数据库连接
if globalSession == nil {
var err error
globalSession, err = mgo.Dial(":27017")
if err != nil {
return nil, err
}
// 可选。将会话切换为单调行为。
globalSession.SetMode(mgo.Monotonic, true)
}
return globalSession.Copy(), nil
}
这个方法很好用,但我遇到的问题是MongoDB有204个连接的限制,然后它开始拒绝连接,报错信息为connection refused because too many open connections: 204
。然而,问题是由于我调用了session.Copy()
,它只返回一个会话而不是错误。所以即使连接被拒绝,我的程序也没有抛出错误。
现在我考虑的是只使用一个会话,而不是使用Copy()
,这样我就可以获得连接错误,代码如下:
var session *mgo.Session = nil
func NewSession() (*mgo.Session, error) {
if session == nil {
session, err = mgo.Dial(url)
if err != nil {
return nil, err
}
}
return session, nil
}
现在我对此的问题是,如果我尝试并发使用同一个会话,会发生什么。
英文:
So I'm having some trouble figuring out best practices for using concurrency with a MongoDB in go. My first implementation of getting a session looked like this:
var globalSession *mgo.Session
func getSession() (*mgo.Session, error) {
//Establish our database connection
if globalSession == nil {
var err error
globalSession, err = mgo.Dial(":27017")
if err != nil {
return nil, err
}
//Optional. Switch the session to a monotonic behavior.
globalSession.SetMode(mgo.Monotonic, true)
}
return globalSession.Copy(), nil
}
This works great the trouble I'm running into is that mongo has a limit of 204 connections then it starts refusing connections connection refused because too many open connections: 204
;however, the issue is since I'm calling session.Copy()
it only returns a session and not an error. So event though the connection refused my program never thrown an error.
Now what I though about doing is just having one session and using that instead of copy so I can have access to a connection error like so:
var session *mgo.Session = nil
func NewSession() (*mgo.Session, error) {
if session == nil {
session, err = mgo.Dial(url)
if err != nil {
return nil, err
}
}
return session, nil
}
Now the problem I have with this is that I don't know what would happen if I try to make concurrent usage of that same session.
答案1
得分: 1
关键是复制会话,然后在使用完毕后关闭它。
func GetMyData() []myMongoDoc {
sessionCopy, _ := getSession() // 来自上面的问题
defer sessionCopy.Close() // 这是重要的部分
results := make([]myMongoDoc, 0)
sessionCopy.DB("myDB").C("myCollection").Find(nil).All(&results)
return results
}
话虽如此,看起来 mgo 实际上并没有暴露对底层连接的控制(请参阅维护该库的 Gustavo Niemeyer 的评论)。一个会话基本上等同于一个连接,但即使在会话上调用 Close()
,mgo 仍然保持连接处于活动状态。从周围的阅读中,似乎 Clone()
可能是一个可行的方法,因为它重用底层套接字,这将避免创建新套接字的三次握手(有关差异的更多讨论,请参见这里)。
还可以参考这个SO答案,描述了处理会话的标准模式。
英文:
The key is to duplicate the session and then close it when you've finished with it.
func GetMyData() []myMongoDoc {
sessionCopy, _ := getSession() // from the question above
defer sessionCopy.Close() // this is the important bit
results := make([]myMongoDoc, 0)
sessionCopy.DB("myDB").C("myCollection").Find(nil).All(&results)
return results
}
Having said that it looks like mgo doesn't actually expose control over the underlying connections (see the comment from Gustavo Niemeyer who maintains the library). A session pretty much equates to a connection, but even if you call Close()
on a session mgo keeps the connection alive. From reading around it seems that Clone()
might be the way to go, as it reuses the underlying socket, this will avoid the 3 way handshake of creating a new socket (see here for more discussion on the difference).
Also see this SO answer describing a standard pattern to handle sessions.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论