Featured image of post Air:Go 本地开发热重载工具使用记录

Air:Go 本地开发热重载工具使用记录

记录 Air 在 Go Web 服务本地开发里的配置方式,包括 .air.toml、环境变量、Docker 开发环境和常见小坑。

以前写 Go 服务,本地调试基本就是:

1
go run main.go

改完代码以后手动停掉,再重新跑一遍。

项目小的时候还行,接口多了以后就有点烦。尤其是调 Web API、后台任务、配置解析这些东西,频繁重启很影响节奏。后来我在几个 Go 项目里用了 Air,主要目的就一个:文件变化以后自动重新编译和启动。

Air 本身没有什么复杂概念,它做的事情很直接:

  • 监听文件变化。
  • 重新执行构建命令。
  • 杀掉旧进程。
  • 启动新进程。

我后来发现,Air 最值钱的地方不是“热重载”这三个字,而是让本地启动变成一种项目约定。只要 .air.toml 写在仓库里,新人不需要问入口在哪、构建产物放哪、改配置会不会重启。

安装和初始化

官方 README 里给的方式比较多,我一般直接用 go install

1
go install github.com/air-verse/air@latest

装完以后确认一下:

1
air -v

如果命令找不到,大概率是 $GOPATH/bin 没有放进 PATH。Windows 下可以看一下:

1
go env GOPATH

在项目根目录执行:

1
air init

它会生成一个 .air.toml 文件。这个文件就是 Air 的主要配置。

我常用的配置

我一般不会直接用默认配置跑所有项目,至少会改这几个地方:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
root = "."
tmp_dir = "tmp"

[build]
cmd = "go build -o ./tmp/server ./cmd/server"
bin = "./tmp/server"
include_ext = ["go", "yaml", "yml", "toml", "tmpl", "html"]
exclude_dir = ["tmp", "vendor", ".git", "node_modules"]
exclude_regex = ["_test.go"]
delay = 800
stop_on_error = true
send_interrupt = true
kill_delay = "500ms"

[log]
time = true

cmd 是构建命令,不一定非得是 go build 当前目录。比如项目入口在 cmd/server,就写成:

1
cmd = "go build -o ./tmp/app ./cmd/server"

tmp_dir 建议单独放一个目录,不要把构建产物扔在项目根目录。这个目录后面也要加到 .gitignore

1
tmp/

include_ext 控制监听哪些后缀。如果只是 Go 服务,go 就够了。如果接口模板、页面模板也在项目里,可以加上 htmltmpltpl

exclude_dir 也很关键。像 tmpvendortestdata 这种目录没必要监听,不排除的话容易出现重复构建或者无意义重启。

如果团队里有人在 Windows 上开发,我会特别确认路径和进程退出行为。Air 本身跨平台,但你的服务是否能正确响应中断、端口是否及时释放,才是本地体验的关键。

启动和环境变量

配置好以后,项目根目录执行:

1
air

之后改 Go 文件,Air 会自动重新编译。

有些服务启动时依赖环境变量,比如配置文件路径、运行环境、端口。

Linux/macOS 可以这样:

1
APP_ENV=local CONFIG=./config/local.yaml air

Windows PowerShell 可以这样:

1
2
3
$env:APP_ENV="local"
$env:CONFIG="./config/local.yaml"
air

Air 不负责替你管理配置,它只是负责重新构建和重启进程。

和 Docker 开发环境一起用

如果 Go 服务跑在容器里,也可以在容器里装 Air。更常见的做法是写一个开发用 docker-compose:

1
2
3
4
5
6
7
8
9
services:
  api:
    image: golang:1.24
    working_dir: /app
    volumes:
      - .:/app
    command: air
    ports:
      - "8080:8080"

这种方式适合团队统一开发环境,但也有代价:Windows/macOS 通过挂载目录时,有时会比直接在宿主机跑慢一点。

几个踩过的小问题

第一个问题是端口占用。如果服务退出不干净,新进程启动时会报端口被占用。Windows 下可以查:

1
netstat -ano | findstr :8080

第二个问题是构建产物被监听。如果 tmp 没有放进 exclude_dir,构建生成文件后又触发一次文件变化,可能会造成重复构建。

第三个问题是入口路径写错。有些项目入口不在根目录,而是在 cmd/apicmd/server,那 .air.toml 里的 cmd 就不要写成 go build -o ./tmp/app .

Air 适合自动重启服务,但它不是测试框架。单元测试、集成测试还是应该用 go testgotestsum 或 CI 单独处理。

我也不会在生产镜像里装 Air。开发镜像和生产镜像最好分开,前者追求方便,后者追求可控和干净。把开发工具混进生产镜像,后面排安全扫描和镜像体积问题会比较烦。

参考资料

本文采用 CC BY-NC-SA 4.0 协议,转载请注明出处。
记录工程实践和排障过程。
使用 Hugo 构建
主题 StackJimmy 设计