关于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())。

相关推荐
LJianK16 小时前
Java中的类、普通类,抽象类,接口的区别
java·开发语言
Dev7z6 小时前
基于MATLAB的5G物理层文本传输系统仿真与性能分析
开发语言·5g·matlab
小智社群6 小时前
贝壳获取小区的名称
开发语言·前端·javascript
s19134838482d7 小时前
vlan实验报告
运维·服务器·网络
lsx2024067 小时前
Python3 OS模块详解
开发语言
LiLiYuan.7 小时前
【Java线程 vs 虚拟机线程】
java·开发语言
FlDmr4i287 小时前
.NET 10 & C# 14 New Features 新增功能介绍-扩展成员Extension Members
开发语言·c#·.net
原来是猿7 小时前
Linux进程信号详解(三):信号保存
开发语言·c++·算法
2402_881319307 小时前
跨服务通信兜底机制-Java 回传失败无持久重试队列,报告可能静默丢失。
java·开发语言·python
格林威7 小时前
SSD 写入速度测试命令(Linux)(基于工业相机高速存储)
linux·运维·开发语言·人工智能·数码相机·计算机视觉·工业相机