fx 框架上手 - 基礎篇

FunTester發表於2024-07-29

在現代軟體開發中,依賴注入(Dependency Injection,簡稱 DI)已成為一種不可或缺的設計模式和程式設計正規化。它不僅能夠提高程式碼的可維護性和可測試性,還能幫助開發者構建更加靈活、松耦合的系統。本文將帶您深入瞭解依賴注入的核心概念,探討它如何改變我們設計和實現軟體的方式,並透過實際的程式碼示例,展示如何在專案中有效地應用這一技術。

相信各位對 依賴注入 不會陌生,相信大多數使用 Java 或者其他 JVM 語言作為主力語言的測試同行來說,更多經驗是集中在 Spring 框架學習和使用當中。在JavaSpring框架中,依賴注入是構建靈活、可維護應用程式的核心技術。SpringIoC容器透過構造器注入、Setter注入或欄位注入等方式自動管理物件間的依賴關係。開發者使用@Autowired@Component等註解或 XML 配置來宣告依賴和元件,讓 Spring 負責物件的建立和生命週期管理。這種方法不僅簡化了程式碼結構,還提高了應用的可測試性和模組化程度,使得 Java 開發者能夠專注於業務邏輯的實現,而不必手動處理複雜的物件依賴關係。

而對於 Go 語言來說,顯然沒有類似 Spring 一統天下的局面。對於提升程式設計效率,可以說是百家爭鳴,好不熱鬧。在 Go 語言中,雖然沒有像 JavaC# 那樣內建的依賴注入框架,但依賴注入的需求同樣存在。開發者通常需要手動注入依賴項,這種方式在應用規模擴大後變得繁瑣且易出錯。fx 框架提供了一種自動化和模組化的依賴注入方式,使開發者可以更專注於業務邏輯,而不是依賴管理。

其中 fx 顯得比較耀眼奪目,下面我們來開始 fx 框架的學習。

fx 介紹

fx 框架是由 Uber 開發的。為了應對自身複雜的分散式系統和微服務架構的需求,Uber 開發了許多開源工具和框架,其中包括 fxfx 框架主要用於簡化 Go 語言應用程式的依賴注入和生命週期管理,並且已經在 Uber 內部和外部的許多專案中得到了廣泛應用。

fx 框架是一個用於構建 Go 應用程式的依賴注入框架,它簡化了應用程式的初始化、啟動和停止過程。fx 透過自動管理依賴關係,使開發者能夠專注於業務邏輯,而無需手動處理依賴注入。fx 提供了模組化的依賴注入方式,透過 fx.Provide 註冊依賴項,透過 fx.Invoke 呼叫需要的元件。同時,fx.Infx.Out 結構體幫助開發者更方便地宣告和管理依賴項,支援按名稱和分組注入。fx.Lifecyclefx 的核心功能之一,它允許開發者在應用程式的不同生命週期階段執行特定邏輯。透過 fx.Hook,可以在應用啟動和停止時執行初始化和清理操作,如連線資料庫、啟動後臺任務等。

fx 的模組化設計使其易於擴充套件和維護,透過將各個功能模組化,開發者可以靈活地組合和重用不同的元件。總之,fx 提供了簡潔且強大的工具,使得構建複雜的 Go 應用程式更加高效和可維護。

fx 依賴注入

首先我們來先看一個 fx 框架啟動的例子。

func main() {  
    app := fx.New() //建立一個fx.App例項  
    app.Run()       //執行fx.App例項  
}

這是一個標準的語法,所有的應用都可以用這個語法進行建立和啟動。其中 fx.New() 方法為: func New(opts ...Option) *App {} ,其中引數 Option 是我們後面主要學習物件。

說到 依賴注入 ,我首先意識到兩個概念,就是依賴物件的提供者和使用者。得有一些物件的建立需要依賴其他物件,然後還需要提供被依賴物件,然後透過 fx 框架將這些複雜的邏輯關係進行管理,並且提供簡單的 API 給使用者。

fx 的核心功能是依賴注入,它簡化了依賴項的管理和注入過程,主要透過以下 API 實現:

  • fx.Provide:用於註冊提供者函式,這些函式會返回應用程式中需要的依賴項。

下面我們透過一個例子來演示 fx 如何進行簡單依賴注入:

package main  

import (  
    "go.uber.org/fx"  
    "go.uber.org/zap")  

func main() {  
    app := fx.New( //建立fx.App例項  
       fx.Provide(NewTester, func() *Age {  
          return &Age{Num: 18} //提供Age例項  
       }, func() *zap.Logger {  
          production, _ := zap.NewProduction() //提供zap.Logger例項  
          return production  
       }), //提供NewTester函式  
    )  
    app.Run() //執行fx.App例項  
}  

type Age struct {  
    Num int //年齡,整型  
}  

type Tester struct {  
    Log *zap.Logger //日誌  
    Age *Age        //年齡  
}  

func NewTester(age *Age, log *zap.Logger) *Tester {  
    return &Tester{  
       Age: age,  
       Log: log,  
    }  

}

這段程式碼展示瞭如何使用 UberFx 框架進行依賴注入和應用程式啟動。程式碼透過 fx.Provide 提供了三個建構函式:一個用於 Age 例項,一個用於 zap.Logger 例項,另一個用於 Tester 例項。然後透過 fx.New 建立一個 Fx 應用,並透過 app.Run() 執行這個應用。下面是更詳細的解釋:

  1. 依賴注入
    • fx.Provide(NewTester, ...):透過 fx.Provide 註冊三個建構函式。
    • NewTester:這是一個建構函式,接受 *Age*zap.Logger 作為引數,並返回一個 *Tester
    • 匿名函式 func() *Age:提供一個 *Age 例項,設定 Num18
    • 匿名函式 func() *zap.Logger:建立並返回一個 zap.Logger 例項,用於日誌記錄。
  2. 執行應用
    • app.Run():啟動 Fx 應用。Fx 將根據註冊的建構函式自動注入依賴,並呼叫相應的初始化邏輯。
  3. 型別定義
    • Age:一個簡單的結構體,包含一個 Num 欄位,用於表示年齡。
    • Tester:一個結構體,包含兩個欄位:Log(型別為 *zap.Logger)和 Age(型別為 *Age)。它們將由 Fx 框架自動注入。
  4. 建構函式 NewTester
    • 接受 *Age*zap.Logger 作為引數,並返回一個 *Tester 例項。

這個例子中,既可以將建立方法傳給 fx.Provide 也可以使用匿名方法,相比較來說是靈活的。這裡不建議使用匿名方法,因為寫多了容易亂,特別是對於 zap.Logger 這種物件來講,真實的建立程式碼可能超過 20 行,用匿名方法更是災難了。

生命週期管理

fx.LifecycleUber Fx 框架中用於管理應用程式生命週期的一部分。它允許你在應用程式的啟動和停止階段執行特定的邏輯。fx.Lifecycle 提供了一種新增啟動和停止鉤子的機制,使你能夠在應用程式的不同階段執行初始化和清理工作。

這裡用到了 fx.Invoke 方法,顧名思義,就是呼叫某些方法,可以傳入已有的方法名也可以使用匿名方法(不建議)。 fx 宣告週期管理中 Hook 就是透過這個 API 實現的,案例如下:

package main  

import (  
    "context"  
    "go.uber.org/fx"    "go.uber.org/zap")  

func main() {  
    app := fx.New( //建立fx.App例項  
       fx.Provide(NewTester, func() *Age {  
          return &Age{Num: 18} //提供Age例項  
       }, func() *zap.Logger {  
          production, _ := zap.NewProduction() //提供zap.Logger例項  
          return production  
       }), //提供NewTester函式  
       fx.Invoke(register), //呼叫register函式  
    )  
    app.Run() //執行fx.App例項  
}  

type Age struct {  
    Num int //年齡,整型  
}  

type Tester struct {  
    Log *zap.Logger //日誌  
    Age *Age        //年齡  
}  

func NewTester(age *Age, log *zap.Logger) *Tester {  
    return &Tester{  
       Age: age,  
       Log: log,  
    }  

}  

func register(lifecycle fx.Lifecycle, tester *Tester) {  
    lifecycle.Append(fx.Hook{  
       OnStart: func(context.Context) error {  
          tester.Log.Info("Starting server")  
          return nil  
       },  
       OnStop: func(context.Context) error {  
          tester.Log.Info("Stopping server")  
          return nil  
       },  
    })  
}

新增的 register 方法,透過 fx.Lifecycle 在應用程式啟動和停止時執行一些自定義邏輯。具體的含義如下:

  1. register 函式
    • 這個函式接收兩個引數:
      • lifecycle fx.Lifecycle:用於管理應用程式的生命週期。
      • tester *Tester:一個包含 zap.LoggerAge 的結構體例項。
  2. lifecycle.Append(fx.Hook{...})
    • lifecycle.Append 方法用於嚮應用程式的生命週期中新增鉤子。
    • fx.Hook 結構體包含兩個回撥函式:OnStartOnStop,分別在應用程式啟動和停止時呼叫。
  3. OnStart 函式
    • OnStart: func(context.Context) error { ... }
      • 這是一個在應用程式啟動時執行的回撥函式。
      • 這個函式記錄一條日誌資訊 "Starting server",表示伺服器正在啟動。
      • 函式返回 nil,表示啟動過程中沒有發生錯誤。
  4. OnStop 函式
    • OnStop: func(context.Context) error { ... }
      • 這是一個在應用程式停止時執行的回撥函式。
      • 這個函式記錄一條日誌資訊 "Stopping server",表示伺服器正在停止。
      • 函式返回 nil,表示停止過程中沒有發生錯誤。

UberFx 框架中,fx.Hookfx.Lifecycle 通常一起使用,用於管理應用程式的生命週期和執行特定的初始化或清理邏輯。下面分別介紹它們的使用場景:

fx.Lifecycle 的使用場景

  1. 管理資源生命週期
    • 資料庫連線:在應用程式啟動時建立資料庫連線,在停止時關閉連線。
    • 快取初始化:在應用程式啟動時載入和初始化快取,在停止時清理快取。
    • 訊息佇列連線:在應用程式啟動時連線訊息佇列,在停止時斷開連線。
  2. 服務啟動和停止
    • Web 伺服器:在應用程式啟動時啟動 Web 伺服器,在停止時優雅地關閉伺服器。
    • 定時任務:在應用程式啟動時啟動定時任務,在停止時停止定時任務。
  3. 日誌記錄和監控
    • 在應用程式的不同階段記錄日誌,如 "應用啟動" 和 "應用停止"。
    • 在應用程式啟動和停止時傳送監控指標,如 CPU 使用率、記憶體使用等。

fx.Hook 的使用場景

  1. 自定義初始化和清理邏輯
    • 啟動時
      • 初始化資料庫:在應用程式啟動時初始化資料庫連線池。
      • 載入配置:讀取和載入應用程式的配置檔案。
      • 註冊 HTTP 路由:在應用程式啟動時註冊各種 HTTP 路由和中介軟體。
    • 停止時
      • 關閉資料庫連線:優雅地關閉資料庫連線。
      • 清理資源:釋放所有的資源,確保應用程式停止時不留下任何未處理的事務。
  2. 啟動和停止通知
    • 在應用程式啟動時傳送通知,如透過郵件或訊息佇列通知團隊。
    • 在應用程式停止時執行最後的清理工作,併傳送應用程式關閉通知。
  3. 除錯和審計
    • 記錄應用程式啟動和停止時的除錯資訊,幫助排查問題。
    • 在停止時記錄審計日誌,如記錄哪些資源被關閉或清理了。

相信透過基礎的學習,已經對 fx 有了瞭解,並且可以著手構建測試專案了。下期我們繼續分享 fx 上手進階內容。

  • 2021 年原創合集
  • 2022 年原創合集
  • 2023 年原創合集
  • 服務端功能測試
  • 效能測試專題
  • Java、Groovy、Go、Python
  • 單元&白盒&工具合集
  • 測試方案&BUG&爬蟲&UI 自動化
  • 測試理論雞湯
  • 社群風采&影片合集
如果覺得我的文章對您有用,請隨意打賞。您的支援將鼓勵我繼續創作!
打賞支援
暫無回覆。

相關文章