关于Go的init函数执行顺序#黑魔法

又又又学到新东西

1. 先来看一段代码

go 复制代码
var _ = func() struct{} {
	http.DefaultTransport = &http.Transport{
		Proxy:               http.ProxyFromEnvironment, //支持系统代理
		MaxIdleConns:        400,                       // 最大空闲连接数
		MaxIdleConnsPerHost: 80,                        // 保留80个空闲连接复用
		MaxConnsPerHost:     150,                       // 限制每个主机的最大连接数
		IdleConnTimeout:     90 * time.Second,
		TLSHandshakeTimeout: 10 * time.Second,
		ForceAttemptHTTP2:   true,
	}
	return struct{}{}
}()

乍一看,我去这是什么用法?

是不是很像 var num int 这一种全局变量的用法呢,没错其实这一种用法,和这个全局定义十分类似,这其实是一个匿名自执行函数 + 包级变量初始化的组合技。

拆开来看:

部分 作用
func() struct{} { ... } 定义一个匿名函数,返回一个空的 struct{}
(...)() 立即执行这个匿名函数
var _ = 把执行结果赋值给一个"下划线"变量(丢弃结果,只为触发副作用)
整体 在包初始化(package init)阶段就强制执行这个函数

结论:这段代码会在当前包被导入或程序启动时,自动执行一次,把 http.DefaultTransport 替换成我们自定义配置好的 Transport。

2. 为什么不直接写成 init() 函数?

有两种常见写法是等价的:

写法1(你看到的这种):

go 复制代码
var _ = func() struct{} {
    http.DefaultTransport = &http.Transport{...}
    return struct{}{}
}()

写法2(更直观的):

go 复制代码
func init() {
    http.DefaultTransport = &http.Transport{...}
}

两者执行时机完全一样,但是在真实场景下,还是建议使用第一种方式,原因有几个:

  1. 多个 init(),他们的执行顺序是不确定的

    Go 允许一个包有多个 init() 函数,执行顺序不保证。如果你想明确控制"这个初始化必须最先执行",用 var _ = func()...() 写在文件最上面就能强制排在所有 init() 前面(因为 var 初始化比 init() 更早)。

  2. 避免污染 init() 命名空间

    有些团队觉得 init() 太多会乱,喜欢用这种"无名"方式。

  3. 看起来更"一次性"

    一眼就看出这代码只运行一次,没副作用返回。

3. 这么改 DefaultTransport 有什么实际意义?

http.DefaultClient(也就是大家经常直接用的 http.Get、http.Post)底层就是用的 http.DefaultTransport

如果你不改,默认值大概是:

go 复制代码
MaxIdleConns:        100
MaxIdleConnsPerHost: 2   // ←←← 这才是最致命的!!
IdleConnTimeout:     90s

MaxIdleConnsPerHost 默认只有 2!

意味着你同一个主机(比如 api.ai.com),即使开了几百个 goroutine 并发请求,最多也只复用 2 个空闲连接,其他的都会重新建连接 → 性能极差,容易把对端打挂,或者被限流。

改成 80~100 之后,才真正发挥出 Go 高并发优势,典型生产环境配置:

go 复制代码
MaxIdleConns:        400-1000   // 总空闲连接池大小
MaxIdleConnsPerHost: 50-200    // 单个主机复用连接数(关键!)
MaxConnsPerHost:     0 或很大  // 0 表示不限制(Go 1.11+ 推荐)

一句话总结:这是 Go 高并发服务必备的"全局连接池优化"黑魔法,几乎所有中大型 Go 项目启动文件里都有这一段(或等价的 init())。

相关推荐
Evand J1 小时前
【TCN与LSTM例程】TCN(时间卷积网络)与LSTM(长短期记忆)训练单输入单输出,用于拟合一段信号,便于降噪。MATLAB
网络·人工智能·matlab·lstm
橘子真甜~1 小时前
C/C++ Linux网络编程6 - poll解决客户端并发连接问题
服务器·c语言·开发语言·网络·c++·poll
9***Y482 小时前
Java开发工具IntelliJ IDEA技巧
java·开发语言·intellij-idea
码力码力我爱你2 小时前
C++性能基准测试
开发语言·c++
张人玉2 小时前
C#WPF——MVVM框架编写管理系统所遇到的问题
开发语言·c#·wpf·mvvm框架
java1234_小锋2 小时前
讲讲Mybatis的一级、二级缓存?
java·开发语言·mybatis
z***I3942 小时前
JavaScript原型链
开发语言·前端·javascript
x***58703 小时前
JavaScript语音识别开发
开发语言·javascript·语音识别
小年糕是糕手3 小时前
【C++】C++入门 -- 输入&输出、缺省参数
c语言·开发语言·数据结构·c++·算法·leetcode·排序算法