效率工具:使用 air 熱重載 Go 應用程序
在項目開發階段,熱重載技術非常有用,通過熱重載,可以實現在無需手動干預的情況下,修改代碼文件後,自動重啓 Go 應用。這極大地提升了開發體驗,同時也節約了我們的開發時間。本文我們一起來體驗一下使用 air 熱重載 Go 應用程序,提高開發效率。
簡介
air 是爲 Go 應用開發設計的一款支持熱重載的命令行工具。
以下是 air 官方總結的特色:
-
• 彩色的日誌輸出
-
• 自定義構建或必要的命令
-
• 支持外部子目錄
-
• 在 Air 啓動之後,允許監聽新創建的路徑
-
• 更棒的構建過程
快速開始
接下來,我們就通過快速開始來體驗一下 air 的便捷與強大。
安裝
首先我們來安裝 air,命令如下。
NOTE:
建議使用 go 1.23 或更高版本
$ go install github.com/air-verse/air@latest
air 的安裝很簡單,因爲是使用 Go 語言開發的,所以直接通過 go install
命令即可安裝。
這裏沒有特別指定安裝某個具體版本,而是安裝 latest
版本,因爲這是一款開發工具,一般不太會出現 API 頻繁變更的情況,所以就選擇使用最新版本。
使用
準備以下目錄結構。
$ tree -F fly
fly/
├── README.md
└── main.go
現在我們來編寫代碼體驗一下 air,main.go
代碼如下。
package main
import (
"fmt"
"net/http"
"os"
"os/signal"
"syscall"
)
func main() {
// 註冊優雅退出信號
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
// 初始化 HTTP 服務器
server := &http.Server{Addr: ":8080"}
// 定義路由
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "🚀 Hello Air! (PID: %d)", os.Getpid()) // PID 用於驗證熱替換
})
// 啓動服務協程
gofunc() {
fmt.Printf("Server started at http://localhost:8080 (PID: %d)\n", os.Getpid())
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
fmt.Printf("Server error: %v\n", err)
}
}()
// 阻塞等待終止信號
<-signalChan
fmt.Println("Server shutting down...")
server.Close()
}
這是一個簡單的 Web Server 程序,根路由 /
的處理函數中獲取並返回了當前程序的 pid
。
在項目根目錄 fly/
下直接執行 air
命令。
可以看到,只需要簡單的 air
命令,我們的 Go 程序就啓動起來了。
打開新的終端,使用 curl
命令訪問 http://localhost:8080。
如圖所示,我們得到了正確的響應。
現在,我們嘗試將根路由處理函數中的響應內容從 Hello Air!
改成 Hello Fly!
並保存程序。
air
會自動重啓我們的 Web Server 程序。
重新使用 curl
命令訪問 http://localhost:8080。
響應結果已經變了,並且程序的 pid
也發生了變化,說明 Go 程序真的被重啓了。
沒錯,air 的使用就是這麼簡單。
那麼接下來,我們再將如下內容,寫入 README.md
中並保存文件。
# Air 熱重載演示項目
## 快速開始
### 1. 安裝 Air
```bash
$ go install github.com/air-verse/air@latest
2. 使用 Air
$ cd /path/to/your_project
$ air
可以發現,這次 `air` 並沒有重啓 Go 程序。

想來這也合情合理,`README.md` 文件並不是 Go 程序代碼,不會影響程序執行結果,所以無需重啓。
不過有一種情況則需要考慮,如果我們提供一個接口,可以返回靜態的 `README.md` 文件內容,那麼 `README.md` 文件修改,就需要重啓 Go 程序。至於如何實現,看完後文的講解,相信你能夠找到答案。
現在,我們按下 `Ctrl + C` 結束進程。

細心的你也許已經發現,我們的 `main.go` 代碼在實現優雅退出後會輸出一條日誌 `Server shutting down...`,可是現在終端中並沒有輸出。
要如何解決這個問題呢?咱們接着往下看。
### 使用進階
上一小節我們快速體驗了 air,本小節再來看下 air 還支持哪些高級功能。
#### 自定義配置
air 支持很多高級功能,這些功能都可以通過配置文件中的配置項來開啓或關閉。
air 的配置文件格式問 `toml`,我們有兩種方式來獲得 air 配置文件模板。
一種是 air 官方提供了 example 配置樣例,你可以在此查看 https://github.com/air-verse/air/blob/master/air_example.toml。
另外一種是使用 `air init` 命令來自動生成。
$ air init
__ _ ___ / /\ | | | |) //--\ || || _ v1.61.7, built with Go go1.24.0
.air.toml file created to the current directory with the default settings
在項目根目錄下執行 `air init` 後,會生成叫 `.air.toml` 的配置文件。
air 的全量配置內容如下,我對其做了詳細的中文註釋。
Air 熱重載工具的 TOML 格式配置文件
完整文檔參考:https://github.com/air-verse/air
工作目錄
支持相對路徑(.)或絕對路徑,注意後續目錄必須在此目錄下
root = "."
air 執行過程中產生的臨時文件存儲目錄
tmp_dir = "tmp"
[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
編譯生成的二進制文件路徑
bin = "tmp/main"
自定義運行參數(可設置環境變量)
full_bin = "APP_ENV=dev APP_USER=air ./tmp/main"
傳遞給二進制文件的運行參數(示例將執行 './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
是否跟蹤符號鏈接目錄,允許 Air 跟蹤符號鏈接(軟鏈接)指向的目錄/文件變化,適用於項目依賴外部符號鏈接資源的場景
follow_symlink = true
日誌文件存儲路徑(位於 tmp_dir 下)
log = "air.log"
是否使用輪詢機制檢測文件變化(替代 fsnotify),Air 默認使用的跨平臺文件監控庫(基於 Go 的 fsnotify 包),通過操作系統事件實時感知文件變化
poll = false
輪詢檢測間隔(默認最低 500ms)
poll_interval = 500# ms
文件變動後的延遲構建時間(防止高頻觸發)
delay = 0# ms
構建出錯時是否終止舊進程
stop_on_error = true
是否發送中斷信號再終止進程(Windows 不支持)
send_interrupt = false
發送中斷信號後的終止延遲
kill_delay = 500# nanosecond
當程序退出時,是否重新運行二進制文件(適合 CLI 工具)
rerun = false
重新運行的時間間隔
rerun_delay = 500
[log]
是否顯示日誌時間戳
time = false
僅顯示主日誌(過濾監控/構建/運行日誌)
main_only = false
禁用所有日誌輸出
silent = false
[color]
主日誌顏色(支持 ANSI 顏色代碼)
main = "magenta"
文件監控日誌顏色
watcher = "cyan"
構建過程日誌顏色
build = "yellow"
運行日誌顏色
runner = "green"
[misc]
退出時自動清理臨時目錄(tmp_dir)
clean_on_exit = true
[screen]
重建時清空控制檯界面
clear_on_rebuild = true
保留滾動歷史(不清屏時有效)
keep_scroll = true
[proxy]
啓用瀏覽器實時重載功能
參考:https://github.com/air-verse/air/tree/master?tab=readme-ov-file#how-to-reload-the-browser-automatically-on-static-file-changes
enabled = true
代理服務器端口(Air 監控端口),瀏覽器連接到 proxy_port,Air 將請求轉發到應用的真實端口 app_port
proxy_port = 8090
應用實際運行端口(需與業務代碼端口一致)
app_port = 8080
全量配置看起來很多,不過別被嚇到。其實這些配置很好理解,並且我都寫了詳細的註釋。
我們可以發現配置項是按功能進行分類的,有如下幾塊配置。
* • 全局配置:`root` 和 `tmp_dir` 分別表示項目的工作目錄和臨時目錄。
* • `build` 類配置:都是與構建相關的配置項。
* • `log` 類配置:與日誌相關的配置項。
* • `color` 類配置:與輸出顏色相關的配置項。
* • `misc` 類配置:雜項配置,其實只有一個 `clean_on_exit` 配置可以用來清理臨時目錄。
* • `screen` 類配置:控制檯相關的配置項。
* • `proxy` 類配置:代理相關的配置項。
其實分析完了這些配置項,你就能夠發現,我們常用的配置其實也就是 `build` 配置項下的那幾個,其他的等用到了再去研究不遲。
我們通常可以重點關注這幾個配置項:
[build]
主構建命令(支持常規 shell 命令或 make 工具)
cmd = "go build -o ./tmp/main ."
從 cmd
編譯生成的二進制文件路徑
bin = "tmp/main"
自定義運行參數(可設置環境變量)
full_bin = "APP_ENV=dev APP_USER=air ./tmp/main"
傳遞給二進制文件的運行參數(示例將執行 './tmp/main hello world')
args_bin = ["hello", "world"]
`cmd`、`bin`、`args_bin` 3 者正是對應了 Go 應用構建、執行、和傳遞命令行參數的功能。所以這也是最常用的 3 項配置。`full_bin` 則是高階版的 `bin`,可以設置環境變量。
這裏需要注意,`cmd` 不僅支持 `go build` 構建命令,它還支持常規的 `shell` 命令和 `make` 命令,這爲應用構建提供了更多的靈活性。
此外 `misc` 配置項下的 `clean_on_exit` 配置也比較有用,將其設置爲 `true` 可以自動清理 `tmp_dir` 目錄產生的臨時文件。
學習了 air 的配置項,接下來我們來解決程序退出日誌 `Server shutting down...` 未輸出的問題。
首先將以上配置信息保存在項目根目錄 `fly/` 下的 `.air.toml` 文件中。
接着,修改如下這兩項配置。
[build] kill_delay = "1s" send_interrupt = true
開啓 `send_interrupt` 配置項後,`air` 在終止我們的 Go 程序之前,會向其發送 `Ctrl + C` 信號,`kill_delay` 配置項保證 `air` 命令爲 Go 程序預留足夠多的退出時間,給程序優雅退出的機會。
再次使用 `air` 命令啓動程序。

然後修改任意一行代碼,我們立刻會看到重啓時 `Server shutting down...` 日誌被輸出了。最後按下 `Ctrl + C` 結束進程,同樣會得到 `Server shutting down...` 日誌輸出。並且等待 1s 過後 `air` 纔會輸出 `see you again~` 並退出。
至於其他配置項,就交給你自行去探索了(思考下,現在你知道如何讓 `air` 監聽 `MarkDown` 文件了嗎)。
#### 命令行參數
`air` 命令不僅支持使用配置文件來開啓或關閉功能,它也支持直接通過命令行參數的方式來開啓或關閉某項功能。
在前文中我們已經使用過 `air init` 來創建配置文件,現在來看看 `air` 還支持哪些命令行參數。
$ air -h Usage of air:
If no command is provided air will start the runner with the provided flags
Commands: init creates a .air.toml file with default settings to the current directory
Flags: -build.args_bin string Add additional arguments when running binary (bin/full_bin). ... -c string config path ... -v show version
執行 `air -h` 或 `air --help` 即可查看命令行幫助信息。篇幅所限,這裏省略了大部分輸出。
不過根據現有的輸出內容我們不難發現,`air` 可以使用 `-c` 參數指定配置文件(默認讀取執行命令的當前目錄下 `.air.toml` 配置文件);`-v` 參數可以輸出版本;`-build.args_bin` 的作用實際上與配置文件中 `[build]` 分類下 `args_bin` 配置項相同。
根據幫助信息的輸出內容我們可以總結出,實際上 `air` 所支持的命令就是配置文件中所有的配置項對應的功能。所以學習完 `air` 的配置文件,再來看 `air` 的命令行參數幾乎沒有學習成本。
那麼現在,我們還有最後一個問題需要解決,既然 `air` 即支持命令行參數,又支持配置文件,那麼如果同時設置二者,誰的優先級更高呢?
我們一起來做一個實驗,修改 Go 程序的 `main.go` 文件,在 `main` 函數的第一行加上如下代碼。
fmt.Printf("args[1]: %v\n", os.Args[1])
這行代碼可以打印 Go 程序接收到的第一個命令行參數。
執行 `air -build.args_bin xxx` 命令啓動程序。

在輸出日誌中可以看到,我們順利得到了命令行參數 `xxx`。
接下來我們修改 `.air.toml` 中如下配置 項。
[build] args_bin = ["yyy"]
現在我們執行 `air -build.args_bin xxx -c .air.toml` 命令啓動程序。

這一次,我們既指定了命令行參數 `-build.args_bin xxx`,又指定了配置文件 `-c .air.toml`。根據輸出日誌可以發現,`air` 的命令行參數優先級高於配置文件。
這一結論符合直覺,也符合絕大多數 Go 命令行程序的設定,我們可以作爲經驗記下來。
那麼現在如果直接使用 `air -c .air.toml` 命令啓動程序。

輸出結果已經不言自明瞭。
對於 `air` 命令行參數的講解就到這裏,其他參數讀者可以自行嘗試。
### 總結
在開發階段使用熱重載技術,可以提高我們的開發效率和提升開發體驗。
air 是使用 Go 語言編寫的一款強大的熱重載工具,使用起來非常便捷,只需要一個簡單的 `air` 命令即可啓動。air 支持通過配置文件或命令行參數來開啓或關閉某項功能。推薦使用 air 來熱重載你的 Go 應用程序。
本文示例源碼我都放在了 GitHub 中,歡迎點擊查看。
希望此文能對你有所啓發。
**延伸閱讀**
* • air GitHub 源碼:https://github.com/air-verse/air
* • air Documentation:https://github.com/air-verse/air/blob/master/README-zh_cn.md
* • air 配置文件示例:https://github.com/air-verse/air/blob/master/air_example.toml
* • 06 | 項目初始化(下):給新項目添加初始文件:https://articles.zsxq.com/id_0z7eii1hgsp0.html
* • 本文 GitHub 示例代碼:https://github.com/jianghushinian/blog-go-example/tree/main/air/fly
* • 本文永久地址:https://jianghushinian.cn/2025/04/03/air/
**聯繫我**
* • 公衆號:Go 編程世界
* • 微信:jianghushinian
* • 郵箱:jianghushinian007@outlook.com
* • 博客:https://jianghushinian.cn
* • GitHub:https://github.com/jianghushinian
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/_eD8Wd0MjYkqKG71rCx3-Q