golang-uber-fx

golang-uber-fx

熱門

使用 uber-go/fx 的 Golang 應用程式框架 — fx.New、fx.Provide、fx.Invoke、fx.Module、fx.Lifecycle 鉤子、fx.Annotate(name/group/As)、fx.Decorate、fx.Supply、fx.Replace、fx.WithLogger 以及支援訊號處理的 Run()。適用於使用或採用 uber-go/fx 的情況,當程式碼匯入 `go.uber.org/fx`,或使用 fx.New 進行服務接線時。若只需原始 DI 而無生命週期,請參閱 `samber/cc-skills-golang@golang-uber-dig` 技能。

2261星標
150分支
更新於 2026/6/6
SKILL.md
readonlyread-only
name
golang-uber-fx
description

使用 uber-go/fx 的 Golang 應用程式框架 — fx.New、fx.Provide、fx.Invoke、fx.Module、fx.Lifecycle 鉤子、fx.Annotate(name/group/As)、fx.Decorate、fx.Supply、fx.Replace、fx.WithLogger 以及支援訊號處理的 Run()。適用於使用或採用 uber-go/fx 的情況,當程式碼匯入 `go.uber.org/fx`,或使用 fx.New 進行服務接線時。若只需原始 DI 而無生命週期,請參閱 `samber/cc-skills-golang@golang-uber-dig` 技能。

角色設定: 你是一位使用 fx 建構長期執行服務的 Go 架構師。你在組合根(composition root)進行圖形接線,將生命週期邏輯推入鉤子而非 init(),並將模組視為可重複使用的單位。

在 Go 中使用 uber-go/fx 進行應用程式接線

應用程式框架,結合了基於反射的 DI 容器(建構於 uber-go/dig 之上)與生命週期、模組系統、支援訊號處理的執行迴圈以及結構化事件日誌。適用於啟動順序、優雅關機和模組化組合至關重要的長期執行服務。

官方資源:

此技能並非詳盡無遺。請參閱函式庫文件和程式碼範例以獲取更多資訊。Context7 可作為探索平台提供協助。如需 Go 套件文件、版本、符號和已知漏洞,→ 請參閱 samber/cc-skills-golang@golang-pkg-go-dev 技能。

go get go.uber.org/fx

fx 與 dig 的比較

fx 建構於 dig 之上,並共用相同的反射式容器引擎。DI 基本元素(ProvideInvokeIn/Out 結構體、命名值、值群組)完全相同 — fx.In/fx.Outdig.In/dig.Out 的重新匯出。

fx 在此基礎上新增的功能:

關注點 dig fx
DI 容器 dig.New() ✅(內嵌)
生命週期鉤子 fx.Lifecycle OnStart/OnStop
模組系統 fx.Module 搭配作用域裝飾器
支援訊號處理的執行迴圈 app.Run() 阻塞等待 SIGINT/SIGTERM
結構化事件日誌 fx.WithLogger / fxevent
啟動/關機逾時 fx.StartTimeout / fx.StopTimeout

選擇 fx 用於長期執行的服務(HTTP 伺服器、工作者、守護程式)— 生命週期和訊號處理是必備的,而模組使大型服務圖形易於管理。

選擇原始 dig 用於無需框架的接線:CLI 工具、向呼叫者暴露容器的函式庫、測試工具,或將 DI 嵌入到自行管理生命週期的現有應用程式中。請參閱 samber/cc-skills-golang@golang-uber-dig 技能。

應用程式

import "go.uber.org/fx"

app := fx.New(
    fx.Provide(NewLogger, NewDatabase, NewServer),
    fx.Invoke(RegisterRoutes),
)
app.Run() // 阻塞直到 SIGINT/SIGTERM,然後執行 OnStop 鉤子

啟動階段:fx.New 驗證型別(建構函式不會執行);app.Start(ctx) 執行每個 fx.Invoke 並以拓撲順序觸發 OnStart 鉤子;main 阻塞在 app.Done()app.Stop(ctx) 以反向順序觸發 OnStop 鉤子。預設逾時為 15 秒 — 使用 fx.StartTimeout / fx.StopTimeout 覆寫。

Provide 和 Invoke

fx.New(
    fx.Provide(NewLogger, NewDatabase, NewServer),  // 惰性
    fx.Invoke(RegisterRoutes, StartMetricsExporter), // 在 Start 期間總是執行
)

fx.Provide 註冊建構函式;fx.Invoke 是觸發器 — 如果沒有 Invoke(直接或間接)引用某個型別,其建構函式永遠不會執行。

生命週期鉤子

注入 fx.Lifecycle 並附加鉤子。建構函式應快速返回;長時間執行的工作應放在 OnStart 中。

func NewHTTPServer(lc fx.Lifecycle, log *zap.Logger, cfg *Config) *http.Server {
    srv := &http.Server{Addr: cfg.Addr}

    lc.Append(fx.Hook{
        OnStart: func(ctx context.Context) error {
            ln, err := net.Listen("tcp", srv.Addr)
            if err != nil { return err }
            go srv.Serve(ln)         // 在 goroutine 中執行阻塞工作
            return nil
        },
        OnStop: func(ctx context.Context) error {
            return srv.Shutdown(ctx)
        },
    })
    return srv
}

兩個回呼都接收一個受 StartTimeout/StopTimeout 限制的 context — 請尊重取消訊號。OnStart 必須快速返回 — 為阻塞工作啟動 goroutine;否則啟動會掛起,且依賴的鉤子永遠不會觸發。

fx.StartHook / fx.StopHook / fx.StartStopHook 可適應更簡單的簽名(無 context、無錯誤,或兩者皆有):

lc.Append(fx.StartStopHook(srv.Start, srv.Stop))   // 配對使用

參數和結果物件

fx 將 dig 的 dig.In / dig.Out 重新匯出為 fx.In / fx.Out。當建構函式有 4 個以上依賴項,或需要使用 name/group/optional 標籤時,請使用它們。

type ServerParams struct {
    fx.In

    Logger *zap.Logger
    DB     *sql.DB
    Cache  *redis.Client     `optional:"true"`
    Routes []http.Handler    `group:"routes"`
}

func NewServer(p ServerParams) *Server { /* ... */ }

fx.Annotate

fx.Annotate 包裝建構函式以新增標籤或介面綁定,而無需 fx.Out 結構體。對於簡潔的 name/group/As 綁定,優先使用它:

fx.Provide(
    fx.Annotate(NewPrimaryDB, fx.ResultTags(`name:"primary"`)),
    fx.Annotate(NewPostgresDB, fx.As(new(Database))),    // 暴露介面
    fx.Annotate(NewUserHandler,
        fx.As(new(http.Handler)),
        fx.ResultTags(`group:"routes"`),
    ),
)

值群組

多個建構函式,一個消費者切片 — 典型用於路由、健康檢查、指標收集器:

type RouteResult struct {
    fx.Out
    Handler http.Handler `group:"routes"`
}

type ServerParams struct {
    fx.In
    Routes []http.Handler `group:"routes"`
}

附加 ,flattengroup:"routes,flatten")以展開切片而非巢狀化。順序不保證 — 當順序重要時,請提供明確的有序切片。

fx.Module

fx.Module 將提供者、調用和裝飾器分組在一個名稱下。模組將裝飾器的作用域限制在自身及其子模組內 — 在 fx.Module("db", ...) 中重新命名的記錄器僅對該模組內的程式碼生效。

var DatabaseModule = fx.Module("database",
    fx.Provide(NewConnection, NewUserRepository),
    fx.Decorate(func(log *zap.Logger) *zap.Logger {
        return log.Named("db")
    }),
)

func main() {
    fx.New(
        fx.Provide(NewConfig, NewLogger),
        DatabaseModule,
        HTTPModule,
    ).Run()
}

將每個模組視為一個可被提升到另一個應用程式的小型函式庫 — 其公開表面是它 Provide 的型別。

有關 fx.Supply/fx.Replace/fx.Decorate、可選依賴項、自訂日誌、手動生命週期和快速參考,請參閱 advanced.md

最佳實踐

  1. 保持 main() 精簡 — 提供者、模組和單一的 Run()。將實際工作推入模組,以便每個模組可以獨立測試。
  2. 使用生命週期鉤子代替 init() 或從建構函式啟動的 goroutine — Start/Stop 順序取決於圖形拓撲,但 init() goroutine 不依賴此順序,這會導致競爭條件和資源洩漏。
  3. OnStart 必須立即返回 — 長時間的工作應放在鉤子內的 goroutine 中。阻塞的 OnStart 會掛起其餘的啟動過程。
  4. 在鉤子中尊重 ctx.Done() — 忽略取消訊號的鉤子會被報告為逾時失敗,但其 goroutine 會繼續執行,導致資源洩漏。
  5. 按模組分組,而非按層分組 — 一個模組擁有單一關注點(HTTP、DB、指標)的提供者、生命週期和裝飾器。
  6. 使用 fx.Annotate 進行標籤綁定,而不是將建構函式包裝在 fx.Out 結構體中 — 這使建構函式在 fx 外部也可重複使用。
  7. 對於預先建構的值(設定、命令列標誌),使用 fx.Supply 取代 fx.Provide。更簡潔,明確表達意圖。
  8. 在 CI 中透過在 fx.New(...).Err() 下啟動來驗證圖形 — 在部署前捕獲遺失的提供者和循環依賴。

常見錯誤

錯誤 修正
在 OnStart 中直接執行長時間工作 在 OnStart 內啟動 goroutine;鉤子本身必須快速返回,以便依賴的鉤子可以執行。
使用 fx.Provide 提供應為 fx.Supply 的內容 預先建構的值(設定、機密)應屬於 fx.Supply — 更清晰且避免無操作的建構函式。
模組裝飾器洩漏到兄弟模組 fx.Module(...) 內部進行裝飾 — 裝飾器僅流向後代。頂層的 fx.Decorate 是全域的。
假設群組有序 群組是無序的。如果順序重要,請從一個建構函式提供有序切片。
建構函式帶有副作用 副作用應屬於 OnStart — 建構函式應輕量且純粹,因為它們可能並發且惰性地執行。
忘記 fx.Invoke 如果沒有 Invoke(或下游消費者),建構函式永遠不會執行。每個應用程式至少新增一個 Invoke。

測試

使用 go.uber.org/fx/fxtest 將 fx 與 *testing.T 整合(失敗時呼叫 t.FatalRequireStop 註冊為 t.Cleanup)。fx.Populate(&target) 從圖形中取出值;fx.Replace 將真實依賴項替換為假物件。完整模式請參閱 testing.md

延伸閱讀

  • advanced.md — Supply/Replace/Decorate、可選依賴項、自訂事件日誌、手動生命週期、完整快速參考
  • recipes.md — 完整的 HTTP 服務搭配資料庫/指標、具優雅關機的背景工作者、相同介面的多個實作、用於 CLI 嵌入的手動生命週期
  • testing.md — fxtest 模式、fx.Replacefx.Populate、隔離的生命週期測試、CI 圖形驗證

交叉參考

  • → 請參閱 samber/cc-skills-golang@golang-uber-dig 技能以了解底層容器、dig.In/dig.Out 和無生命週期的 DI
  • → 請參閱 samber/cc-skills-golang@golang-dependency-injection 技能以了解 DI 概念和函式庫比較
  • → 請參閱 samber/cc-skills-golang@golang-samber-do 技能以了解基於泛型且無反射的替代方案
  • → 請參閱 samber/cc-skills-golang@golang-google-wire 技能以了解編譯時 DI(無執行時容器)
  • → 請參閱 samber/cc-skills-golang@golang-structs-interfaces 技能以了解介面設計模式
  • → 請參閱 samber/cc-skills-golang@golang-context 技能以了解 OnStart/OnStop 鉤子中的 context 傳播
  • → 請參閱 samber/cc-skills-golang@golang-testing 技能以了解一般測試模式

如果您在 uber-go/fx 中遇到錯誤或非預期行為,請在 https://github.com/uber-go/fx/issues 開啟問題。