在Go中“显式关闭”mgo MongoDB

在Go中“显式关闭”mgo MongoDB,mongodb,go,mgo,Mongodb,Go,Mgo,我有一个Docker集群,运行10个相同类型的web服务。他们都在使用MongoDB,以及其他用于数据持久性的工具 这是在服务启动时从main调用的代码: // Init establishes a connection with MongoDB instance. func Init(mongoURL string) *mgo.Session { mongo, err := mgo.Dial(mongoURL) misc.PanicIf(err) // make su

我有一个Docker集群,运行10个相同类型的web服务。他们都在使用MongoDB,以及其他用于数据持久性的工具

这是在服务启动时从main调用的代码:

// Init establishes a connection with MongoDB instance.
func Init(mongoURL string) *mgo.Session {
    mongo, err := mgo.Dial(mongoURL)
    misc.PanicIf(err)

    // make sure we are strongly consistent
    mongo.SetMode(mgo.Strong, true)

    // update global state
    db = mongo
    Entries = db.DB("").C("entries")
    Channels = db.DB("").C("channels")
    Settings = db.DB("").C("settings")
    Metadata = db.DB("").C("metadata")

    // only use this on first load, to confirm the settings are there
    // every refresh should be done via UpdateGlobalSettings (thread-safe)
    GlobalSettings = &GlobalSettingsStruct{}
    GlobalSettings.Init()

    return mongo
}
因此,基本上API和Worker只使用全局变量,如条目、设置等

运行一段时间后,服务停止正常工作。每个mongo操作,例如条目。查找。。。返回错误:显式关闭

这是什么意思?我应该定期刷新mongoDB连接,还是应该关闭它并与每个请求重新建立连接


该应用程序以性能为导向,因此,尽管mongo连接已关闭,但由于所有内容都在内存或群集缓存中运行,因此所有内容仍在运行。我不想做一些会延迟处理的愚蠢的事情。

首先,尝试在mgo中启用调试模式,以便获得有关正在发生的事情的更多信息

我认为服务器在一段不活动时间后正在断开连接。 在任何情况下,通常的方法是进行mgo拨号,然后在每个请求开始时使用中间件复制连接

皮埃特罗

没关系。而是使用Session.Copy创建新的连接实例,而不是直接使用返回的会话。golang mongodb驱动程序包中有连接池。

当未使用sess.SetPoolLimit50时,当mgo处于压力下时会发生许多错误,例如10.000个并发连接

当我限制游泳池时,错误消失了

我已经为下面这个问题创建了一个测试用例源代码,所以您可以在自己的机器上轻松地测试它

我想听听你对这种行为有什么建议

package main

import (
    "fmt"
    "time"

    // you can also use original go-mgo/mgo here as well
    mgo "github.com/globalsign/mgo"
    "github.com/globalsign/mgo/bson"
)

// TODO: put some records into db first:
//
// use testapi
// db.competitions.insert([
//   {game_id: 1, game_name: "foo"},
//   {game_id: 2, game_name: "bar"},
//   {game_id: 3, game_name: "jazz"}
// ])

// NOTE: you might want to increase this depending on your machine power
//       mine is:
//         MacBook (Retina, 12-inch, Early 2015)
//         1,2 GHz Intel Core M
//         8 GB 1600 MHz DDR3
const ops = 10000

type m bson.M

func main() {
    sess, err := mgo.DialWithTimeout("localhost", time.Second)
    if err != nil {
        panic(err)
    }
    defer sess.Close()

    // NOTE: without this setting there are many errors
    //       see the end of the file
    // setting pool limit prevents most of the timeouts
    // sess.SetPoolLimit(50)

    // sess.SetSocketTimeout(60 * time.Second)
    sess.SetMode(mgo.Monotonic, true)

    time.Sleep(time.Second)

    done := make(chan bool, ops)

    for i := 0; i < ops; i++ {
        go func() {
            defer func() { done <- true }()

            result, err := fetchSomething(sess)
            if err != nil {
                fmt.Printf("ERR: %s\n", err)
            }
            fmt.Printf("RESULT: %+v\n", result)
        }()
    }

    for i := 0; i < ops; i++ {
        <-done
    }
}

func fetchSomething(sess *mgo.Session) ([]m, error) {
    s := sess.Copy()
    defer s.Close()

    result := []m{}

    group := m{
        "$group": m{
            "_id": m{
                "id":   "$game_id",
                "name": "$game_name",
            },
        },
    }

    project := m{
        "$project": m{
            "_id":  "$_id.id",
            "name": "$_id.name",
        },
    }

    sort := m{
        "$sort": m{
            "_id": 1,
        },
    }

    err := col(s, "competitions").Pipe([]m{group, project, sort}).All(&result)
    if err != nil {
        return nil, err
    }
    return result, nil
}

// col is a helper for selecting a column
func col(sess *mgo.Session, name string) *mgo.Collection {
    return sess.DB("testapi").C(name)
}

/*


ERRORS WITHOUT sess.SetPoolLimit(50)


$ go run socket_error.go
RESULT: [map[name:foo _id:1] map[_id:2 name:bar] map[_id:3 name:jazz]]
ERR: read tcp 127.0.0.1:52918->127.0.0.1:27017: read: connection reset by peer
ERR: write tcp 127.0.0.1:52084->127.0.0.1:27017: write: broken pipe
RESULT: []
RESULT: []
ERR: write tcp 127.0.0.1:53627->127.0.0.1:27017: write: broken pipe
ERR: write tcp 127.0.0.1:53627->127.0.0.1:27017: write: broken pipe
RESULT: []
ERR: write tcp 127.0.0.1:53627->127.0.0.1:27017: write: broken pipe
RESULT: []
ERR: write tcp 127.0.0.1:53627->127.0.0.1:27017: write: broken pipe
RESULT: []
ERR: write tcp 127.0.0.1:53627->127.0.0.1:27017: write: broken pipe
RESULT: []
ERR: write tcp 127.0.0.1:53627->127.0.0.1:27017: write: broken pipe
RESULT: []
RESULT: []
ERR: read tcp 127.0.0.1:53627->127.0.0.1:27017: read: connection reset by peer
RESULT: []
ERR: resetNonce: write tcp 127.0.0.1:53625->127.0.0.1:27017: write: broken pipe
RESULT: []
RESULT: [map[name:foo _id:1] map[_id:2 name:bar] map[_id:3 name:jazz]]
ERR: resetNonce: write tcp 127.0.0.1:54591->127.0.0.1:27017: write: broken pipe
RESULT: []
...
...
*/