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 的一些想法,请参阅此主题:
我在 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 的一些想法,请参阅此主题: