同一个服务有多个进程。

通过 strace 命令发现,除了时钟调用以外,就是 futex 调用,说明系统在等待锁的释放。
我们发现这种情况发生在平滑重启以后,因此之前认为是老的进程没有退出导致的。

通过 dlv attach 6164 命令,进入 dlv 控制台。
输入 grs,列出所有的 goroutines。

我们发现有很多的 goroutines, goroutine 1 是 main goroutine,可以简单理解为通过 main.main 启动了程序,派生出了其他的 goroutine。
我们切换到 goroutine 1 来查看堆栈信息,看下发生了什么。
运行命令 gr 1,切换到 goroutine 1。
运行命令 bt,查看堆栈信息。

我们发现程序阻塞在了 statf.go:125,我们通过输入 c 命令,让程序继续运行,发现程序继续锁死,无法继续。

判断确实是在这里卡死了程序,我们看下这一行代码:
func (s *StatFHelper) pushBackMsg(stStatInfo StatInfo, fromServer bool) {
if fromServer {
s.chStatInfoFromServer <- stStatInfo
} else {
s.chStatInfo <- stStatInfo // 程序卡在了这里
}
}
结合前边 grs 命令的结果,我们发现这里的 s.chStatInfo 是 nil,所以程序卡死在了这里。

如果 StatReport 是 nil,则程序不会进入到上报这一行,因此,StatReport 有值,但是 StatReport.chStatInfo 是 nil 才可能出现这种情况。
我们看下 StatReport 的初始化:

146 行 StatReport 进行了初始化,147 行进行了 StatReport.chStatInfo 的赋值,如果 goroutine 在 146 行以后进行了切换,就有可能造成上述情况的发生。
为什么会出现这种情况?
我们来看下 StatReport 的整个初始化过程:

可以发现,StatReport 的初始化另外单独启动了一个 goroutine,而非同步进行的。
当我们初始化配置的时候,需要调用 tars 的 config 服务获取配置,而每个 tars 请求,都会调用 StatReport.pushBackMsg 来上报此次请求是否成功。
当我们调用了 tars 时,刚好发生了 StatReport 未完全初始化,导致了程序卡死在这里,无法继续运行,但是因为不是所有的 goroutine 都停掉了,因此 go 程序不会 panic(如果所有的 goroutine 都阻塞了,go 程序会 panic)。
通过对多个服务的观察,我们发现这些服务的现象是相同的,都是卡在了上报状态过程中。
知道了上述问题产生的原因,解决起来就很简单了。
本人在社区群反馈后,TarsGo 官方已经在 https://github.com/TarsCloud/TarsGo/pull/505 这次 PR 中进行了该问题的修复。
暂时没有留言