12
0
0

解决Go的热重载问题:air的简单说明

2026-08-18
2026-08-18
文章摘要
|

我是经常写 Go 的,每次改完一段代码之后,都得重新敲go run ... ,挺麻烦的。于是我在想有没有一种东西可以帮我实现监听文件变化,一旦有变更就重新编译运行。幸运的是有前人已经写好了这种东西,也就是本文要介绍的东西:air。

https://github.com/air-verse/air

air 是一款非常非常轻便且易用的热重载工具,要使用它很简单,分两步就可以了。

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

  2. 去项目根目录,直接输入air

结束了,它会自动开始监听文件变化进行热重载。

当然它的功能不可能就这么简单。

air的配置文件

刚刚我们输入air 的时候,它会先找有没有配置文件,如果没有,则它会调用默认的配置文件。

我们可以用air init 去让其生成一个名为.air.toml 的配置文件。它大概长这样子:

# 配置文件格式
root = "."              # 项目根目录
tmp_dir = "tmp"         # 临时构建文件目录

[build]
cmd = "go build -o ./tmp/main ."   # 构建命令
bin = "tmp/main"                    # 可执行文件路径
include_ext = ["go", "tpl", "tmpl", "html"]  # 热重载监听的文件后缀
exclude_dir = ["assets", "tmp", "vendor"]    # 排除的目录
exclude_file = []                   # 排除的文件
follow_symlink = true
delay = 1000                        # debounce 毫秒数,避免多次触发

[color]
main = "yellow"
watcher = "cyan"
build = "green"
runner = "magenta"

当然这不是全部的配置项,全部的配置项列出来的完整文件长这样:

#:schema https://json.schemastore.org/any.json

# Air 的配置文件(TOML 格式)

# 工作目录
# 可以是 "." 或绝对路径,请注意,其下的目录都必须位于 root 之下。
root = "."
tmp_dir = "tmp"

# 移除该项可完全不加载任何文件
# 不存在的文件会被安全忽略
env_files = [".env"]

[build]
# 每次构建前运行的命令数组
pre_cmd = ["echo 'hello air' > pre_cmd.txt"]
# 普通的 shell 命令,你也可以使用 make。
cmd = "go build -o ./tmp/main ."
# 收到 ^C 后运行的命令数组
post_cmd = ["echo 'hello air' > post_cmd.txt"]
# 由 'cmd' 生成的二进制文件,即将弃用,推荐使用 entrypoint。
bin = "tmp/main"
# 相对于 root 的入口二进制文件。第一项是可执行文件,后续项为默认参数。
entrypoint = ["./tmp/main"]
# 自定义二进制运行命令,可以设置运行应用时的环境变量。
full_bin = "APP_ENV=dev APP_USER=air ./tmp/main"
# 运行二进制文件(bin/full_bin)时追加的额外参数。将执行 './tmp/main hello world'。
args_bin = ["hello", "world"]
# 监听的扩展名。使用 ["*"] 监听所有文件扩展名。
include_ext = ["go", "tpl", "tmpl", "html"]
# 忽略的扩展名或目录。
exclude_dir = ["assets", "tmp", "vendor", "frontend/node_modules"]
# 如需指定额外监听目录,可在此添加。
include_dir = []
# 指定要监听的文件。
include_file = []
# 排除的文件。
exclude_file = []
# 使用正则表达式排除特定文件。
exclude_regex = ["_test\\.go"]
# 忽略未发生变更的文件。
exclude_unchanged = true
# 忽略可能引发过度文件监听的危险根目录
ignore_dangerous_root_dir = false
# 是否跟踪目录的符号链接
follow_symlink = true
# 此日志文件会存放在 tmp_dir 中。
log = "air.log"
# 使用轮询检测文件变更,而不是 fsnotify。
poll = false
# 轮询间隔(默认为 500ms 的最小间隔)。
poll_interval = 500 # 毫秒
# 如果文件变更过于频繁,没必要每次变更都触发构建,可设置延时。
delay = 0 # 毫秒
# 当构建出错时,停止运行旧的二进制文件。
stop_on_error = true
# 在杀进程前发送 Interrupt 信号(Windows 上忽略,使用 TASKKILL)
send_interrupt = false
# 发送 Interrupt 信号后的等待延迟
kill_delay = 500 # 纳秒
# 是否重新运行二进制文件
rerun = false
# 每次运行后的延迟
rerun_delay = 500

# 规则:监听文件,并在它们变更时运行一条命令,但不重新构建和重启主程序。
# 适用于资源文件、代码生成等场景。
# 被规则匹配到的文件不会触发重新构建;如果规则的命令生成了被监听的文件(如 .go 文件),
# 则会顺其自然地触发后续的重建。
# [[build.rules]]
# # 规则名称,用于日志输出。默认为 "rule-<索引>"。
# name = "assets"
# # 当匹配的文件变更时要运行的命令。必填。
# cmd = "npm run build"
# # 匹配这些目录下的文件,即使这些目录在 exclude_dir 中也会被包含。
# include_dir = ["web"]
# # 匹配的扩展名。留空则表示匹配任意扩展名。
# include_ext = ["js", "ts", "css"]
# # 匹配的文件名。
# include_file = []
# # 使用正则表达式排除特定文件。
# exclude_regex = []
# # 运行命令前的防抖延迟(毫秒)。
# delay = 1000

# 平台特定的构建覆盖配置
[build.windows]
cmd = "go build -o ./tmp/main.exe ."
bin = "tmp\\main.exe"
entrypoint = ["tmp\\main.exe"]

[log]
# 是否在日志中显示时间
time = false
# 仅显示主日志(隐藏 watcher、build、runner 的日志)
main_only = false
# 静默 Air 产生的所有日志
silent = false

[color]
# 自定义各部分的颜色。如果未指定颜色,则使用应用原始日志的颜色。
main = "magenta"
watcher = "cyan"
build = "yellow"
runner = "green"

[misc]
# 退出时是否删除 tmp 目录
clean_on_exit = true
# 启动时的横幅文字。设为 "" 可隐藏横幅。
# startup_banner = ""

[screen]
clear_on_rebuild = true
keep_scroll = true

[proxy]
# 启用浏览器实时重载。
enabled = true
proxy_port = 8090
app_port = 8080
# 等待应用启动并可用的超时时间(毫秒)。
# 当你的应用启动较慢时(如数据库连接、配置加载等)很有用。
# 代理将在此持续时间内重试连接你的应用,之后放弃。
# 默认 5000ms(5 秒)。如果看到 “unable to reach app” 错误,可增大此值。
app_start_timeout = 5000

当然我们也不需要这么多的配置,按需配置即可。

其实说一个好玩的地方,我们是可以去按需配置build的,所以我们可以设置它为任意一个编译运行命令……所以理论上来说这套系统可以用于所有系统的热重载。

说回正题,如果你觉得 air 这套系统就只停留在二进制层面的热重载,那你就错了,它有一个更强大的地方。

Docker热重载

我是有将自己写的应用打包为 Docker 镜像的需求,但是我不可能改一次就docker build 的。这套系统刚好也覆盖了这一个痛点。

它有两种方式来做到容器内热更新,第一种是镜像部署:

docker/podman run -it --rm \
    -w "<PROJECT>" \
    -e "air_wd=<PROJECT>" \
    -v $(pwd):<PROJECT> \
    -p <PORT>:<APP SERVER PORT> \
    cosmtrek/air \
    -c <CONF>

只需要将你的项目目录挂载上去,就可以实现在容器内的热重载。

你也可以用 Docker Compose file:

services:
  my-project-with-air:
    image: cosmtrek/air
    # working_dir 的值必须和挂载的卷保持一致
    working_dir: /project-package
    ports:
      - <any>:<any>
    environment:
      - ENV_A=${ENV_A}
      - ENV_B=${ENV_B}
      - ENV_C=${ENV_C}
    volumes:
      - ./project-relative-path/:/project-package/

这是使用官方镜像的部署方式。不使用官方镜像也可以做到:

# 选择你想要的版本,>= 1.25
FROM golang:1.25-alpine

WORKDIR /app

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

COPY go.mod go.sum ./
RUN go mod download

CMD ["air", "-c", ".air.toml"]

然后去构建镜像即可。

总之这套系统我是很推荐的,小巧,轻便,并且简单易上手。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或者给予支持!

解决Go的热重载问题:air的简单说明
/archives/jie-jue-gode-re-chong-zai-wen-ti-airde-shi-yong-xin-de
作者
Leave_Time(时泠)
发布于
2026-08-18
许可协议
CC BY-NC-SA 4.0

评论