以前写 Go 服务,本地调试基本就是:
| |
改完代码以后手动停掉,再重新跑一遍。
项目小的时候还行,接口多了以后就有点烦。尤其是调 Web API、后台任务、配置解析这些东西,频繁重启很影响节奏。后来我在几个 Go 项目里用了 Air,主要目的就一个:文件变化以后自动重新编译和启动。
Air 本身没有什么复杂概念,它做的事情很直接:
- 监听文件变化。
- 重新执行构建命令。
- 杀掉旧进程。
- 启动新进程。
我后来发现,Air 最值钱的地方不是“热重载”这三个字,而是让本地启动变成一种项目约定。只要 .air.toml 写在仓库里,新人不需要问入口在哪、构建产物放哪、改配置会不会重启。
安装和初始化
官方 README 里给的方式比较多,我一般直接用 go install:
| |
装完以后确认一下:
| |
如果命令找不到,大概率是 $GOPATH/bin 没有放进 PATH。Windows 下可以看一下:
| |
在项目根目录执行:
| |
它会生成一个 .air.toml 文件。这个文件就是 Air 的主要配置。
我常用的配置
我一般不会直接用默认配置跑所有项目,至少会改这几个地方:
| |
cmd 是构建命令,不一定非得是 go build 当前目录。比如项目入口在 cmd/server,就写成:
| |
tmp_dir 建议单独放一个目录,不要把构建产物扔在项目根目录。这个目录后面也要加到 .gitignore:
| |
include_ext 控制监听哪些后缀。如果只是 Go 服务,go 就够了。如果接口模板、页面模板也在项目里,可以加上 html、tmpl、tpl。
exclude_dir 也很关键。像 tmp、vendor、testdata 这种目录没必要监听,不排除的话容易出现重复构建或者无意义重启。
如果团队里有人在 Windows 上开发,我会特别确认路径和进程退出行为。Air 本身跨平台,但你的服务是否能正确响应中断、端口是否及时释放,才是本地体验的关键。
启动和环境变量
配置好以后,项目根目录执行:
| |
之后改 Go 文件,Air 会自动重新编译。
有些服务启动时依赖环境变量,比如配置文件路径、运行环境、端口。
Linux/macOS 可以这样:
| |
Windows PowerShell 可以这样:
Air 不负责替你管理配置,它只是负责重新构建和重启进程。
和 Docker 开发环境一起用
如果 Go 服务跑在容器里,也可以在容器里装 Air。更常见的做法是写一个开发用 docker-compose:
这种方式适合团队统一开发环境,但也有代价:Windows/macOS 通过挂载目录时,有时会比直接在宿主机跑慢一点。
几个踩过的小问题
第一个问题是端口占用。如果服务退出不干净,新进程启动时会报端口被占用。Windows 下可以查:
| |
第二个问题是构建产物被监听。如果 tmp 没有放进 exclude_dir,构建生成文件后又触发一次文件变化,可能会造成重复构建。
第三个问题是入口路径写错。有些项目入口不在根目录,而是在 cmd/api 或 cmd/server,那 .air.toml 里的 cmd 就不要写成 go build -o ./tmp/app .。
Air 适合自动重启服务,但它不是测试框架。单元测试、集成测试还是应该用 go test、gotestsum 或 CI 单独处理。
我也不会在生产镜像里装 Air。开发镜像和生产镜像最好分开,前者追求方便,后者追求可控和干净。把开发工具混进生产镜像,后面排安全扫描和镜像体积问题会比较烦。