Air不是开箱即用的魔法工具,必须严格对齐root路径、build.bin与build.cmd输出路径、[watch]和[build]两节include_ext配置,否则易静默失败或报permission denied。

air 是目前最稳定、配置可控性最强的 Go 开发期热重载工具,但它不是“开箱即用”的魔法,必须对齐路径、权限和监听范围三处关键点,否则大概率静默失败或报 fork/exec ./tmp/main: permission denied。
为什么 go run 不能替代热加载
每次 go run 都是全新进程,不管理生命周期、不释放端口、不处理信号。改完代码保存后它不会动,你得手动 Ctrl+C 再敲一遍命令——这不是热加载,是手摇式轮询。
-
http.Server若没调Shutdown(),旧进程退出后端口常被卡住,下次go run直接报address already in use - 默认不注册
os.Interrupt或syscall.SIGTERM,air杀进程时旧实例可能僵死成僵尸态 - 无法插入调试参数(如
-gcflags='all=-N -l'),导致Delveattach 失败
air 启动失败的三个硬坑
90% 的问题出在配置不对齐,而非工具本身:
-
root必须指向含go.mod的目录(通常是项目根),不是main.go所在目录;设错会导致go build找不到 module -
build.bin和build.cmd输出路径必须严格一致,例如都用./tmp/main;不一致会报exec: "./tmp/main": file does not exist -
[watch]和[build]两节的include_ext必须完全相同;只在watch里加yaml,但build不认,改配置文件就不会触发重建
监听不到 internal/ 下的改动?检查工作目录和 include_ext
air 默认只监听当前工作目录及子目录下的 .go 文件,不跨模块、不跟进符号链接、不扫描 vendor/ 或隐藏目录。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 如果你的
main.go在cmd/api/main.go,却在项目根目录运行air,那internal/下的改动根本不会被看到 - 正确做法:进入
cmd/api/目录再运行air,或在.air.toml中显式配置watch.cwd = "cmd/api" - 想让
config.yaml变更也触发构建,必须在[watch]和[build]两节都加include_ext = ["go", "yaml", "yml"];Linux 下inotify句柄数有限,别无脑加一堆后缀
Windows 和容器环境的特殊处理
路径和挂载方式稍有偏差就会失效:
- Windows 用户注意:路径一律用正斜杠
/,哪怕在 PowerShell 里写.\tmp\main也会导致监听失效 - 容器内热重载必须配
bind mount+air一起用;单纯在宿主机跑air再docker build是伪热重载,镜像层没更新 - 多
main项目(如cmd/api和cmd/worker)必须指定main_cmd = "go run cmd/api/main.go",否则air默认找main.go会错位
配置热加载不是加个工具就完事,真正容易被忽略的是:每次修改后是否真触发了构建、构建产物有没有执行权限、新进程是否成功接管端口——这些环节一环断,整个流程就卡在“看似在跑,实则没更新”。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










