我在go程序中对api进行了大量的web调用,结果存储在数据库中(使用mgo)。api调用在单独的go例程中完成。在其他例程中,我从数据库中提取信息并对其进行处理,然后更新数据库。当数据被放回时,设置一个标志,这样就知道该数据已经被后处理了,所以当程序向数据库请求另一个条目来后处理时,数据库将返回一个条目,其中标志complete
被设置为false
。当该标志位设置为true时,go例程关闭:wg.done()
.
一切都很好,我有很多打印输出告诉我程序是如何进行的,但是在运行结束时,我得到一个巨大的堆栈跟踪,其中包含许多相同的:
gooutine 56731 [sleep]: time.Sleep(0x12a05f200)/usr/local/Cellar/go/1.5/libexec/src/runtime/time.: 59 + 0 xf9gopkg.in/分别以% 2 ev2。(* mongoServer)。声波发射器(0 xc82601b420 0 x1)/用户/亚历克斯/去/src/gopkg.in/mgo.v2/服务器。Go:295 +0x1b4创建的gopkg.in ev2.newserver/分别以% 2/用户/亚历克斯/去/src/gopkg.in/mgo.v2/服务器。: 88 + 0 x162
gooutine 56698 [sleep]: time.Sleep(0x12a05f200)/usr/local/Cellar/go/1.5/libexec/src/runtime/time.: 59 + 0 xf9gopkg.in/分别以% 2 ev2。(* mongoServer)。声波发射器(0 xc82601bce0 0 x1)/用户/亚历克斯/去/src/gopkg.in/mgo.v2/服务器。Go:295 +0x1b4创建的gopkg.in ev2.newserver/分别以% 2/用户/亚历克斯/去/src/gopkg.in/mgo.v2/服务器。: 88 + 0 x162
gooutline 56699 [sleep]: time.Sleep(0x1dcd6500)/usr/local/Cellar/go/1.5/libexec/src/runtime/time.: 59 + 0 xf9gopkg.in/分别以% 2 ev2。(* mongoCluster) .syncServersLoop (0 xc8256425a0)/用户/亚历克斯/去/src/gopkg.in/mgo.v2/集群。Go:353 +0x2b1创建的gopkg.in ev2.newcluster/分别以% 2/用户/亚历克斯/去/src/gopkg.in/mgo.v2/集群。: 73 + 0 x1a0
gooutine 56738 [sleep]: time.Sleep(0x12a05f200)/usr/local/Cellar/go/1.5/libexec/src/runtime/time.: 59 + 0 xf9gopkg.in/分别以% 2 ev2。(* mongoServer)。声波发射器(0 xc82606fa40 0 x1)/用户/亚历克斯/去/src/gopkg.in/mgo.v2/服务器。Go:295 +0x1b4创建的gopkg.in ev2.newserver/分别以% 2/用户/亚历克斯/去/src/gopkg.in/mgo.v2/服务器。: 88 + 0 x162
下面有一件事,这是堆栈跟踪中与上面的唯一不同的输出(上面只是一个示例,我的终端无法滚动回开始,因为有很多)
gooutine 57201 [IO wait]: net.runtime_pollWait(0xedb6f0, 0x72)0 xc82000a2c0)/usr/local/Cellar/go/1.5/libexec/src/runtime/netpoll.: 157 + 0 x60净。(* pollDesc)。等待(0xc827b0e5a0, 0x72, 0x0, 0x0)/usr/local/Cellar/go/1.5/libexec/src/net/fd_poll_runtime。: 73 + 0 x3a净。(* pollDesc)。WaitRead(0xc827b0e5a0, 0x0, 0x0)/usr/local/Cellar/go/1.5/libexec/src/net/fd_poll_runtime。: 78 + 0 x36净。(* netFD)。读取(0xc827b0e540, 0xc828d61000, 0x1000, 0x1000, 0x0,0 x754050 0 xc82000a2c0)/usr/local/Cellar/go/1.5/libexec/src/net/fd_unix.: 232 + 0 x23a康涅狄格州净。(*)。读取(0xc8260eac38, 0xc828d61000, 0x1000, 0x1000, 0x0, 0x0,0 x0)/usr/local/Cellar/go/1.5/libexec/src/net/net.: 172 + 0 xe4net/http.noteEOFReader。读取(0x7960c0, 0xc8260eac38, 0xc82751fd38,0xc828d61000, 0x1000, 0x1000, 0xc82644dc20, 0x0, 0x0)/usr/local/Cellar/go/1.5/libexec/src/net/http/transport.: 1370 + 0 x67net/http。(* noteEOFReader)。读取(0xc826116e60, 0xc828d61000, 0x1000,0x1000, 0xc827d1a770, 0x0, 0x0):126 +0xd0bufio。(*读者).fill (0 xc82644d4a0)/usr/local/Cellar/go/1.5/libexec/src/bufio/bufio.: 97 + 0 x1e9读者bufio。(*)。Peek(0xc82644d4a0, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0)/usr/local/Cellar/go/1.5/libexec/src/bufio/bufio.: 132 + 0 xccnet/http。(* persistConn) .readLoop (0 xc82751fce0)/usr/local/Cellar/go/1.5/libexec/src/net/http/transport.: 876 + 0 xf7创建的net/http.(*Transport). dialconn/usr/local/Cellar/go/1.5/libexec/src/net/http/transport.: 685 + 0 xc78
我正在努力弄清楚它告诉我的是什么,是否锁定写入数据库,是否例程没有关闭,是否超时,我不知道。我使用go 1.5 btw。
与数据库对话的代码如下:
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),一些后台活动开始保持集群拓扑在内存中的最新,并保持连接池的一致(注意,所有会话创建后,单个Dial
调用将共享一个连接池)。
每次想要与数据库通信时调用Dial
意味着所有的逻辑对于每个拨号都是独立的,并且后台活动可能在最后一个会话关闭后需要一段时间才能关闭。
所以即使它可以工作,它也非常低效,因为它必须再次学习正在使用的拓扑,它无法使用现有池中的连接,并且它还会产生不必要的后台混乱,这就是你在backtrace中观察到的。
关于如何在这种情况下更好地维护会话的一些想法,请参阅本主题:
- 维护mgo会话的最佳实践