init()的执行顺序 && Go scheduler的初始化过程

https://golang.design/go-questions/sched/init/

(任何一个.go文件的)init都先于main执行。


main.go:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package main

import (
"fmt"
"initt/tools"
)

func main() {

fmt.Println("main start~~~")

fmt.Println(tools.Int2Str(21333333333))

}

tools/toolFunc.go:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package tools

import (
"fmt"
"strconv"
)

func init() {

fmt.Println("init start...")

}

func Int2Str(arg int) string {

return strconv.Itoa(arg)
}

运行 go run main.go,结果为:

1
2
3
init start...
main start~~~
21333333333

Shit Mountain遇险记:


(任何一个.go文件的)init都先于main执行。

某陈年老代码,不遵循单一入口,各种中间件的初始化不放在main.go里,而是很多package都有一个init(如 有一个叫做storage文件夹,用来放各种存储。该文件夹下有一个init())…

这样做平时没什么,有一天,需要给mysql,redis,kafka,httpclient等加一个哨兵监控。 需要在mysql,redis初始化时,拿到哨兵的实例,用其中某个collector作为参数传递。。

这个监控的sdk,按常理显然应该在main.go里初始化,而各个包的init,先于main.main执行,于是在在mysql,redis等初始化时,调用哨兵显然为nil。。

最后通过hook,或全局变量予以解决。。当时也有考虑,在init()满天飞的场景下,能否确定哪个init()最先执行? 或者能否也写一个init()初始化哨兵监控 并保证其最先执行?

这其实涉及到runtime ….(待写)

爽哥于深夜


pointer.go:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import (
"fmt"
)

func main() {
var p **int
var i int = 10
var p1 *int = &i
fmt.Println("p1=", p1)

p = &p1
fmt.Println("p=", p)
}

go build -gcflags=all="-N -l" pointer.go

(go build -gcflags=all=”-N -l” 禁用编译器优化和内联函数,在研究runtime时非常有必要 ​​)

Golang init() 函数

Go:神奇的init函数

Go语言goroutine调度器初始化

https://7byte.github.io/2017/05/20/golangPackageInit/

https://studygolang.com/articles/6464

【Golang源码分析】golang的启动原理

Golang后台单元测试实践

基于Golang的云原生日志采集服务设计与实践

Golang 并发编程核心篇 —— 内存可见性

GODEBUG=INITTRACE=1 ./dashen

https://stackoverflow.com/questions/24790175/when-is-the-init-function-run

https://play.golang.org/p/9P-LmSkUMKY

https://xie.infoq.cn/article/7baab69fe385d0c6a1b35fa32

https://zboya.github.io/post/go_scheduler/

https://learnku.com/go/t/47135

https://www.bookstack.cn/read/qcrao-Go-Questions/spilt.10.GC-GC.md

https://blog.leonard.wang/archives/inittask


Init()灵不灵?


不灵!只认init()


golang init的顺序你清楚吗

1
2
3
4
5
6
7
8
9
10
11
是的,我可以解释一下 Go 中 init 函数的顺序。

在 Go 中,init 函数用于初始化包和程序。当一个包被导入时,Go 运行该包中所有的 init 函数。当一个程序启动时,Go 运行主包(即 main 包)中的 init 函数。

init 函数的执行顺序遵循以下规则:

首先会执行依赖包(被当前包导入的其他包)中的 init 函数,且每个包的 init 函数只会执行一次。

如果当前包有多个 init 函数,它们会按照源代码中的顺序依次执行。

总之,Go 保证 init 函数的执行顺序是正确的,开发者无需手动控制 init 函数的执行顺序。

我想把整个项目中所有init的执行顺序打印出来?如何实现?

你可以通过反射(reflection)获取一个包中所有的函数,并筛选出所有以 init 命名的函数,然后按照顺序执行它们并记录执行的顺序。以下是一个示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package main

import (
"fmt"
"reflect"
"sort"
)

func main() {
pkg := "example.com/mypackage" // 你的包名
funcs := []string{}

// 获取包中所有的函数
pkgFuncs := reflect.ValueOf(pkg).Elem().FieldByName("init").FieldByName("i").FieldByName("funcs")
for i := 0; i < pkgFuncs.Len(); i++ {
f := pkgFuncs.Index(i)
funcName := f.FieldByName("name").String()
if funcName == "init" {
continue // 忽略 "init" 函数本身
}
funcs = append(funcs, funcName)
}

// 按照字母顺序排序
sort.Strings(funcs)

// 执行函数并打印执行顺序
fmt.Println("Init functions order:")
for _, funcName := range funcs {
fmt.Println(funcName)
fn := reflect.ValueOf(pkg).MethodByName(funcName)
fn.Call(nil)
}
}

请将 example.com/mypackage 替换为你的包名,然后运行该代码,它将输出所有 init 函数的执行顺序。注意,这个方法只适用于在当前可执行文件中使用的包,如果你想打印整个项目中所有包的 init 函数的执行顺序,你需要在每个包的代码中使用类似的方法并手动整合。

我就是想把整个项目的打印出来?要怎样实现

要实现打印整个项目中所有包的 init 函数的执行顺序,你需要对每个包都使用类似的方法并手动整合它们。

以下是一个示例代码,可以打印整个项目中所有包的 init 函数的执行顺序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package main

import (
"fmt"
"reflect"
"sort"
"strings"
)

func main() {
initOrder := make(map[string]int) // 记录每个包的 init 函数执行顺序
packages := make(map[string]bool) // 记录已经处理过的包
var scan func(pkg string) // 递归扫描包依赖关系

// 初始化扫描函数
scan = func(pkg string) {
if packages[pkg] {
return
}
packages[pkg] = true

// 获取包中所有的函数
pkgFuncs := reflect.ValueOf(pkg).Elem().FieldByName("init").FieldByName("i").FieldByName("funcs")
for i := 0; i < pkgFuncs.Len(); i++ {
f := pkgFuncs.Index(i)
funcName := f.FieldByName("name").String()
if funcName == "init" {
continue // 忽略 "init" 函数本身
}
initOrder[pkg+"."+funcName] = len(initOrder)
}

// 递归扫描依赖包
pkgDeps := reflect.ValueOf(pkg).Elem().FieldByName("imports")
for i := 0; i < pkgDeps.Len(); i++ {
depPkg := pkgDeps.Index(i).String()
if !strings.HasPrefix(depPkg, "vendor/") { // 忽略 vendor 目录下的包
scan(depPkg)
}
}
}

// 扫描主包
scan(".")

// 按照 init 函数执行顺序排序
type initFunc struct {
pkg string
name string
}
funcs := make([]initFunc, len(initOrder))
for k, v := range initOrder {
pkgName, funcName := strings.Split(k, ".")[0], strings.Split(k, ".")[1]
funcs[v] = initFunc{pkgName, funcName}
}

// 打印执行顺序
fmt.Println("Init functions order:")
for _, f := range funcs {
fmt.Printf("%s.%s\n", f.pkg, f.name)
}
}

该代码使用递归方式扫描项目中的所有包,并记录每个包的 init 函数的执行顺序。最后,它按照执行顺序打印出每个包中所有 init 函数的名称。请注意,该代码将忽略 vendor 目录下的包,如果你想包括这些包,可以将代码中的相关部分进行修改。


如何规避滥用init(),如何解决一些初始化配置等的需求?

很多知名项目中,都有这样使用。。比如k8s的第一个版本