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



我在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会话的最佳实践

最新更新