【问题标题】:goroutine stack traces incompletegoroutine 堆栈跟踪不完整
【发布时间】:2017-09-12 19:05:35
【问题描述】:

我使用 golang 编写了一个网络应用程序。当它在生产中运行时,有一些 goroutines 被阻塞。以下是信息(使用pprof生成):

goroutine 792247 [chan receive, 948 minutes]:
database/sql.(*Tx).awaitDone(0xc4206e2b80)
    /usr/local/go/src/database/sql/sql.go:1440 +0x57
created by database/sql.(*DB).begin
    /usr/local/go/src/database/sql/sql.go:1383 +0x274

goroutine 已经在通道上等待了 948 分钟。显然,有什么不对劲。但是堆栈跟踪似乎不完整。对我来说找到错误是不够的。 (我想从我的程序开始一些堆栈跟踪。)

如何获得这个 goroutine 的完整堆栈跟踪? 或者有没有其他方法可以调试这个问题?

更新

我已经阅读了 database/sql/sql.go 的源代码。原来database/sql/sql.go:1440 在一个新的 goroutine 中。堆栈跟踪是incomplete,因为之前的堆栈跟踪属于parent goroutine。

我的问题应该是:有没有更好的方法来调试这个问题?

【问题讨论】:

  • 尝试运行go run -race *.go
  • @Acidic 我已经尝试过了。也许这不是某种竞争条件。还是谢谢。
  • @Eagle /database/sql/sql.go:1440 正在等待提交或回滚事务。您可以检查您的代码是否有未解决的交易。
  • @JohnSPerayil 我检查了我的代码。有很多 API 使用 SQL 事务。但如果没有RollbackCommit,我找不到。这就是为什么我想获得完整的堆栈跟踪。还是谢谢。
  • 公平地说,跟踪并不完整。它被称为go tx.awaitDone()。每个 goroutine 都有自己的堆栈,因此这是您正在检查的 goroutine 堆栈的开头。

标签: performance go


【解决方案1】:

我认为没有任何方法可以获取父 goroutine 堆栈,而无需手动跟踪每个 goroutine 调用并为其生成一个 id。

在这种特定情况下,很可能您的事务尚未提交或回滚,因为发生错误并且函数过早退出而没有调用任何一个。

避免这种情况的一个好模板是使用“defer”。

func (s Service) DoSomething() (err error) {
    tx, err := s.db.Begin()
    if err != nil {
        return
    }
    defer func() {
        if err != nil {
            tx.Rollback()
            return
        }
        err = tx.Commit()
    }()
    if _, err = tx.Exec(...); err != nil {
        return
    }
    if _, err = tx.Exec(...); err != nil {
        return
    }
    // ...
    return }

Code Reference

PS:小心错误阴影。

【讨论】:

  • 谢谢。使用“延迟”要好得多。我只是在每个return 之前添加了RollbackCommit。但是,我想它应该达到与您的代码相同的效果。
  • @Eagle 是的,defer 为相同的功能生成更简洁的代码。在你的情况下,我想不出任何其他阻止 goroutines 的情况,如果我这样做,我会更新答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-06
  • 2016-10-05
  • 1970-01-01
  • 2015-07-01
  • 1970-01-01
  • 2021-08-18
相关资源
最近更新 更多