效率工具:使用 air 熱重載 Go 應用程序

在項目開發階段,熱重載技術非常有用,通過熱重載,可以實現在無需手動干預的情況下,修改代碼文件後,自動重啓 Go 應用。這極大地提升了開發體驗,同時也節約了我們的開發時間。本文我們一起來體驗一下使用 air 熱重載 Go 應用程序,提高開發效率。

簡介

air 是爲 Go 應用開發設計的一款支持熱重載的命令行工具。

以下是 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 程序。

![](https://mmbiz.qpic.cn/mmbiz_png/lBgdOzzGZlRTdfAvqthef7x5Kp5Oov8HvU5CS12E81eNXtNvrNcJYsR9zB0NSKiazTmsfl4rrKeibEIDWzria8Wow/640?wx_fmt=png&from=appmsg)

想來這也合情合理,`README.md` 文件並不是 Go 程序代碼,不會影響程序執行結果,所以無需重啓。

不過有一種情況則需要考慮,如果我們提供一個接口,可以返回靜態的 `README.md` 文件內容,那麼 `README.md` 文件修改,就需要重啓 Go 程序。至於如何實現,看完後文的講解,相信你能夠找到答案。

現在,我們按下 `Ctrl + C` 結束進程。

![](https://mmbiz.qpic.cn/mmbiz_png/lBgdOzzGZlRTdfAvqthef7x5Kp5Oov8H03v0hWwWDgfSiaMPqylCVDGeFNUwOYvFJNwD0j6QFlfgusShz6eF1oA/640?wx_fmt=png&from=appmsg)

細心的你也許已經發現,我們的 `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` 命令啓動程序。

![](https://mmbiz.qpic.cn/mmbiz_png/lBgdOzzGZlRTdfAvqthef7x5Kp5Oov8HVq4JBZ3Ds684MdMaWPMwbyhHjlEGIFpVPibeXopKzw1ZXjtovQBo2iaQ/640?wx_fmt=png&from=appmsg)

然後修改任意一行代碼,我們立刻會看到重啓時 `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` 命令啓動程序。

![](https://mmbiz.qpic.cn/mmbiz_png/lBgdOzzGZlRTdfAvqthef7x5Kp5Oov8HQPkTs85GbBkxVGwD32V3jBiaLubxlJGJeFnvyxwABsUR9fyZWgE8etQ/640?wx_fmt=png&from=appmsg)

在輸出日誌中可以看到,我們順利得到了命令行參數 `xxx`。

接下來我們修改 `.air.toml` 中如下配置 項。

[build]   args_bin = ["yyy"]


現在我們執行 `air -build.args_bin xxx -c .air.toml` 命令啓動程序。

![](https://mmbiz.qpic.cn/mmbiz_png/lBgdOzzGZlRTdfAvqthef7x5Kp5Oov8HxWOSwBkB09tdbzQjVUBAWQb7xaDHbtpPPmAEibxeTStuhTZ6iaBibriapQ/640?wx_fmt=png&from=appmsg)

這一次,我們既指定了命令行參數 `-build.args_bin xxx`,又指定了配置文件 `-c .air.toml`。根據輸出日誌可以發現,`air` 的命令行參數優先級高於配置文件。

這一結論符合直覺,也符合絕大多數 Go 命令行程序的設定,我們可以作爲經驗記下來。

那麼現在如果直接使用 `air -c .air.toml` 命令啓動程序。

![](https://mmbiz.qpic.cn/mmbiz_png/lBgdOzzGZlRTdfAvqthef7x5Kp5Oov8HgeT5UcbvKEs46glquI6KYVoDGIRB3xmptrSFglibwL96lvdvC9416gw/640?wx_fmt=png&from=appmsg)

輸出結果已經不言自明瞭。

對於 `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