24

老弟有空吗,我Go项目里某个init函数被调用了两次!

 4 years ago
source link: http://www.pengrl.com/p/20030/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

周五快要下班了,同事过来找我,说他的Go项目里某个init函数被调用了两次。WTF,这不耽误回家过周末吗!

废话不多说,直接上同事工位看现场。

同事向我展示了两行内容相同的日志,并且与之对应的源码。大概像下面这样:

func init() {
    log.Info("xxx")
}

我让同事将代码中的日志稍微做了些修改。大概像下面这样:

func init() {
    //log.Info("xxx")
    log.Info("yyy")
}

重新运行,打印出的日志变成了两行 yyy ,说明日志确实是由该处产生的。

我怀疑是init函数被手动调用了。由于init函数是小写开头的,包外应该调用不了,所以我让同事在包内搜索是否有手动调用的地方,结果没有。

(事后实验证明,Go不允许手动调用init函数,会产生编译错误)。

于是我又想了想,Go中的init函数会否由于多次import造成多次调用。应该是不会,不然日常很多代码都应该有问题。

由于同事编写代码的环境,编译环境,运行环境可能不在一台机器上,我怀疑是中途某个环节同步代码时出错了。

所以我将代码再次做了些修改。大概像下面这样:

// 声明一个包(全局)作用域的变量
var tmp int

func init() {
    //log.Info("yyy")

    // 此处,我没再用代码中引入的第三方log库,而是使用标准库中的log库,目的是将源码文件打印出来
    log.SetFlags(log.Llongfile)
    // 看两次打印的tmp值,以及对应的地址
    log.Printf("zzz %d %p", tmp, &tmp)
    tmp = 10
}

打印出的结果显示,两次tmp的值都是0,并且地址是不同的。

更为关键的是,打印出的源码文件是不同的!大概像下面这样:

github.com/test/x/config.gogithub.com/test2/x/config.go

到此问题基本定位到了,反馈给同事,同事说项目的git路径确实发生过变化,由 test 变成了 test2 ,于是他做了个软链。。

那么原因也找到了,应该是代码中同时import了 testtest2 导致的。

至于具体是哪个地方import的,我就不关心了。

向同事说明,Go中以package为单位管理代码,两个不同路径的package,即使代码完全相同,依然是两个package。

当有依赖全局状态时,可能就会出现问题。比如同事的业务代码就不允许init被调用两次,根本原因,也基本能猜到是有全局状态。

或者换个角度理解,我添加的代码中,出现了两个tmp可能就会隐藏某些bug。

闪人,回家过周末。

回家的路上,我又想了想,定位这个问题是否有更快的手段。

可以在init函数中把调用堆栈打印出来,但是init函数的调用应该是编译器生成的,在main入口处调用,由于我确认了init函数无法手动调用,所以打堆栈基本也没什么卵用。

使用go list将项目依赖的所有包打印出来,这个方法应该也行得通。

但是前文也说了,由于他们的项目,开发、编译、运行可能不是一个机器,甚至编译时的go命令都是经过封装过的工具,我也绕不清。也就不好去go list。理论上环境单纯的话,在编译机go list也是可以的。

不过这也有点事后诸葛亮,事实上,我最后定位到问题时用的方法,我事先也没有明确猜到是import两处相同代码引起的,我只是隐约怀疑代码在编译过程中发生了拷贝。

原文链接: https://pengrl.com/p/20030/

原文出处: yoko blog ( https://pengrl.com )

原文作者: yoko ( https://github.com/q191201771 )

版权声明:本文欢迎任何形式转载,转载时完整保留本声明信息(包含原文链接、原文出处、原文作者、版权声明)即可。本文后续所有修改都会第一时间在原始地址更新。

ruq2Mbu.jpg!web


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK