发生错误时重新创建mgo会话(读取tcp 127.0.0.1:46954->127.0.0.1:27017:i / o超时)

时间:2016-11-19 23:09:54

标签: mongodb go mgo

我想知道Go使用mgo中的MongoDB会话管理,尤其是关于如何正确确保会话关闭以及如何对写入失败作出反应。

我已阅读以下内容:

Best practice to maintain a mgo session

Should I copy session for each operation in mgo?

但是,不能适用于我的情况。

我有两个goroutines将事件后事件存储到MongoDB中,共享相同的* mgo.Session,两者看起来都像以下一样:

func storeEvents(session *mgo.Session) {
    session_copy := session.Copy()
    // *** is it correct to defer the session close here? <-----
    defer session_copy.Close()
    col := session_copy.DB("DB_NAME").C("COLLECTION_NAME")
    for {
        event := GetEvent()
        err := col.Insert(&event)
        if err != nil {
            // *** insert FAILED - how to react properly? <-----
            session_copy = session.Copy()
            defer session_copy.Close()
        }
    }
}
几小时后

col.Insert(&amp; event)返回错误

read tcp 127.0.0.1:46954->127.0.0.1:27017: i/o timeout

我不确定如何对此做出反应。发生此错误后,它会在所有后续写入时发生,因此我似乎必须创建一个新会话。我的替代品似乎是:

1)重启整个goroutine,即

if err != nil {
    go storeEvents(session)
    return
}

2)创建一个新的会话副本

if err != nil {
    session_copy = session.Copy()
    defer session_copy.Close()
    col := session_copy.DB("DB_NAME").C("COLLECTION_NAME")
    continue
}

- &GT;我使用defer session_copy.Close()的方式是否正确? (注意上面的延迟引用了另一个会话的Close()函数。无论如何,这些会话永远不会被关闭,因为函数永远不会返回。即,随着时间的推移,会创建许多会话而不会关闭。

其他选择?

1 个答案:

答案 0 :(得分:0)

所以我不知道这对你有什么帮助,但我对这个设置没有任何问题。

我有一个我从中导入的mongo包。这是我的mongo.go文件的模板

package mongo

import (
    "time"

    "gopkg.in/mgo.v2"
)

var (
    // MyDB ...
    MyDB DataStore
)

// create the session before main starts
func init() {
    MyDB.ConnectToDB()
}

// DataStore containing a pointer to a mgo session
type DataStore struct {
    Session *mgo.Session
}

// ConnectToTagserver is a helper method that connections to pubgears' tagserver
// database
func (ds *DataStore) ConnectToDB() {
    mongoDBDialInfo := &mgo.DialInfo{
        Addrs:    []string{"ip"},
        Timeout:  60 * time.Second,
        Database: "db",
    }
    sess, err := mgo.DialWithInfo(mongoDBDialInfo)
    if err != nil {
        panic(err)
    }
    sess.SetMode(mgo.Monotonic, true)
    MyDB.Session = sess
}

// Close is a helper method that ensures the session is properly terminated
func (ds *DataStore) Close() {
    ds.Session.Close()
}

然后在另一个包中,例如主根据以下评论更新

package main

import (
    "../models/mongo"
)

func main() {
    // Grab the main session which was instantiated in the mongo package init function
    sess := mongo.MyDB.Session
    // pass that session in
    storeEvents(sess)
}

func storeEvents(session *mgo.Session) {
    session_copy := session.Copy()
    defer session_copy.Close()

    // Handle panics in a deferred fuction
    // You can turn this into a wrapper (middleware)
    // remove this this function, and just wrap your calls with it, using switch cases
    // you can handle all types of errors
    defer func(session *mgo.Session) {
        if err := recover(); err != nil {
            fmt.Printf("Mongo insert has caused a panic: %s\n", err)
            fmt.Println("Attempting to insert again")
            session_copy := session.Copy()
            defer session_copy.Close()
            col := session_copy.DB("DB_NAME").C("COLLECTION_NAME")
            event := GetEvent()
            err := col.Insert(&event)
            if err != nil {
                fmt.Println("Attempting to insert again failed")
                return
            }
            fmt.Println("Attempting to insert again succesful")
        }
    }(session)

    col := session_copy.DB("DB_NAME").C("COLLECTION_NAME")
    event := GetEvent()
    err := col.Insert(&event)
    if err != nil {
        panic(err)
    }
}

我在AWS上的生产服务器上使用类似的设置。我每小时做100多万次插入。希望这可以帮助。我为确保mongo服务器可以处理连接而做的另一件事是增加了我的生产机器上的ulimit。它在stack

中进行了讨论