英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论