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

相关推荐
yaoxin5211239 小时前
397. Java 文件操作基础 - 创建常规文件与临时文件
java·开发语言·python
小短腿的代码世界9 小时前
Qt日志系统深度解析:从qDebug到企业级日志框架
开发语言·qt
REDcker10 小时前
浏览器端Web程序性能分析与优化实战 DevTools指标与工程清单
开发语言·前端·javascript·vue·ecmascript·php·js
ACP广源盛1392462567310 小时前
IX8024与科学大模型的碰撞@ACP#筑牢科研 AI 算力高速枢纽分享
运维·服务器·网络·数据库·人工智能·嵌入式硬件·电脑
Empty-Filled11 小时前
AI生成测试用例功能怎么测:一个完整实战案例
网络·人工智能·测试用例
我命由我1234511 小时前
Kotlin 开发 - lateinit 关键字
android·java·开发语言·kotlin·android studio·android-studio·android runtime
Halo_tjn12 小时前
Java Set集合相关知识点
java·开发语言·算法
码云数智-大飞12 小时前
本地部署大模型:隐私安全与多元优势一站式解读
运维·网络·人工智能
我不是8神12 小时前
面试题:runtime.MAXPROCESS怎么处理?
golang
许彰午12 小时前
我手写了一个 Java 内存数据库(二):B+ 树的插入与分裂
java·开发语言·面试