Mongodb 对例程的查询产生巨大的堆栈跟踪

Mongodb queries on routines produces huge stack trace

我在 go 程序中对 api 进行了大量网络调用,结果存储在数据库中(使用 mgo)。 api 调用是在单独的 go 例程上完成的。在其他例程中,我会在更新数据库之前从数据库中提取信息并对其进行处理。放回数据时,会设置一个标志,以便知道该数据已被 post 处理,因此当程序向数据库请求另一个条目以 post 处理时,数据库会返回一个标志 complete 设置为 false。当标志设置为 true 时,go 例程关闭:wg.done().

一切都很好,我有很多打印输出告诉我程序是如何进行的,但是在它的末尾运行我得到一个包含很多相同内容的巨大堆栈跟踪:

goroutine 56731 [sleep]: time.Sleep(0x12a05f200) /usr/local/Cellar/go/1.5/libexec/src/runtime/time.go:59 +0xf9 gopkg.in/mgo%2ev2.(*mongoServer).pinger(0xc82601b420, 0x1) /Users/alex/go/src/gopkg.in/mgo.v2/server.go:295 +0x1b4 created by gopkg.in/mgo%2ev2.newServer /Users/alex/go/src/gopkg.in/mgo.v2/server.go:88 +0x162

goroutine 56698 [sleep]: time.Sleep(0x12a05f200) /usr/local/Cellar/go/1.5/libexec/src/runtime/time.go:59 +0xf9 gopkg.in/mgo%2ev2.(*mongoServer).pinger(0xc82601bce0, 0x1) /Users/alex/go/src/gopkg.in/mgo.v2/server.go:295 +0x1b4 created by gopkg.in/mgo%2ev2.newServer /Users/alex/go/src/gopkg.in/mgo.v2/server.go:88 +0x162

goroutine 56699 [sleep]: time.Sleep(0x1dcd6500) /usr/local/Cellar/go/1.5/libexec/src/runtime/time.go:59 +0xf9 gopkg.in/mgo%2ev2.(*mongoCluster).syncServersLoop(0xc8256425a0) /Users/alex/go/src/gopkg.in/mgo.v2/cluster.go:353 +0x2b1 created by gopkg.in/mgo%2ev2.newCluster /Users/alex/go/src/gopkg.in/mgo.v2/cluster.go:73 +0x1a0

goroutine 56738 [sleep]: time.Sleep(0x12a05f200) /usr/local/Cellar/go/1.5/libexec/src/runtime/time.go:59 +0xf9 gopkg.in/mgo%2ev2.(*mongoServer).pinger(0xc82606fa40, 0x1) /Users/alex/go/src/gopkg.in/mgo.v2/server.go:295 +0x1b4 created by gopkg.in/mgo%2ev2.newServer /Users/alex/go/src/gopkg.in/mgo.v2/server.go:88 +0x162

在下面的所有这些中有一件事,这是堆栈跟踪上与上面唯一不同的输出(上面只是一个示例,我的终端无法滚动回开头有这么多)

goroutine 57201 [IO wait]: net.runtime_pollWait(0xedb6f0, 0x72, 0xc82000a2c0) /usr/local/Cellar/go/1.5/libexec/src/runtime/netpoll.go:157 +0x60 net.(*pollDesc).Wait(0xc827b0e5a0, 0x72, 0x0, 0x0) /usr/local/Cellar/go/1.5/libexec/src/net/fd_poll_runtime.go:73 +0x3a net.(*pollDesc).WaitRead(0xc827b0e5a0, 0x0, 0x0) /usr/local/Cellar/go/1.5/libexec/src/net/fd_poll_runtime.go:78 +0x36 net.(*netFD).Read(0xc827b0e540, 0xc828d61000, 0x1000, 0x1000, 0x0, 0x754050, 0xc82000a2c0) /usr/local/Cellar/go/1.5/libexec/src/net/fd_unix.go:232 +0x23a net.(*conn).Read(0xc8260eac38, 0xc828d61000, 0x1000, 0x1000, 0x0, 0x0, 0x0) /usr/local/Cellar/go/1.5/libexec/src/net/net.go:172 +0xe4 net/http.noteEOFReader.Read(0x7960c0, 0xc8260eac38, 0xc82751fd38, 0xc828d61000, 0x1000, 0x1000, 0xc82644dc20, 0x0, 0x0) /usr/local/Cellar/go/1.5/libexec/src/net/http/transport.go:1370 +0x67 net/http.(*noteEOFReader).Read(0xc826116e60, 0xc828d61000, 0x1000, 0x1000, 0xc827d1a770, 0x0, 0x0) :126 +0xd0 bufio.(*Reader).fill(0xc82644d4a0) /usr/local/Cellar/go/1.5/libexec/src/bufio/bufio.go:97 +0x1e9 bufio.(*Reader).Peek(0xc82644d4a0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0) /usr/local/Cellar/go/1.5/libexec/src/bufio/bufio.go:132 +0xcc net/http.(*persistConn).readLoop(0xc82751fce0) /usr/local/Cellar/go/1.5/libexec/src/net/http/transport.go:876 +0xf7 created by net/http.(*Transport).dialConn /usr/local/Cellar/go/1.5/libexec/src/net/http/transport.go:685 +0xc78

我正在努力弄清楚它告诉我的是什么,它是否在写入数据库时​​锁定,例程是否未关闭以及是否超时,我不知道。 我正在使用 go 1.5 顺便说一句。

与数据库对话的代码如下:

func (l *Ledger) addRaceToDatabase(race Race) { //true if added, 

false if existed
  session, err := mgo.Dial("127.0.0.1")
  if err != nil {
    panic(err)
  }

  defer session.Close()

  session.SetMode(mgo.Monotonic, true)

  c := session.DB("collection").C("races")

    // Index
  index := mgo.Index{
    Key:        []string{"id"},
    Unique:     true,
    DropDups:   true,
    Background: true,
    Sparse:     true,
  }

  err = c.EnsureIndex(index)
  if err != nil {
    panic(err)
  }

  result := Race{}
  //if the race exists, don't add it to the database
  err = c.Find(bson.M{"id": race.ID}).One(&result)
  if err != nil {
    //if there is an error there wasn't an entry so add this to the database
    err = c.Insert(race)
    if err != nil {
        panic(err)
    }   
  } else {
    //if it does find an entry, it will print it
    fmt.Println("FOUND: ", result.ID)
  }
}

看起来引用的逻辑每次需要用它做某事时都在拨号 MongoDB,这不是在 HTTP 服务器内保持持续数据库通信的合适方式。

每次你 Dial 一个 MongoDB 服务器(或副本集,或 mongos),一些后台 activity 被启动以保持集群拓扑 up-to-date 在内存中,并保持连接池正常(请注意,在单个 Dial 调用后创建的 all session 将共享一个连接池)。

每次想与数据库对话时调用 Dial 意味着所有这些逻辑对于每个表盘都是独立的,并且后台 activity 可能需要一段时间才能关闭最后 session 已关闭。

因此,尽管它可以工作,但效率非常低,因为它必须再次了解正在使用的拓扑结构,它无法使用现有池中的连接,并且还会产生不必要的后台流失,这就是您所观察到的在你的回溯中。

有关如何在这种情况下更好地维护 session 的一些想法,请参阅此主题: