共用方式為


Go 特性管理

Go 功能管理函式庫提供了一種基於功能標誌來開發和公開應用程式功能的方法。 一旦開發新功能之後,許多應用程式都有特殊需求,例如何時應該啟用此功能,以及要在哪些情況下啟用此功能。 這個函式庫提供了一種定義這些關係的方法,並且還整合到常見的Go程式碼模式中,使得公開這些功能成為可能。

特性旗標提供一種方式,讓 Go 應用程式動態開啟或關閉特性。 開發人員可以在簡單的使用案例 (例如,條件陳述式) 中使用功能旗標。

以下是使用 Go 功能管理庫的一些好處:

  • 功能管理的常見慣例
  • 進入門檻較低
    • Azure 應用程式設定功能旗標來源
  • 功能旗標存留期管理
    • 組態值可以即時變更
  • 涵蓋簡單至複雜的案例
    • 開啟/關閉功能
    • 根據對伺服器的呼叫動態評估功能的狀態

Go 功能管理庫是開源的。 如需詳細資訊,請造訪 GitHub 存放庫

功能旗標

功能旗標可以啟用或停用。 旗標的狀態可以透過使用功能篩選來設為有條件的。

功能篩選

功能篩選會定義何時應啟用功能的案例。 評估功能是否開啟或關閉時,會周遊其功能篩選清單,直到其中一個篩選條件決定應啟用功能為止。 此時,功能會被視為已啟用且周遊功能篩選會停止。 如果沒有功能篩選指出應該啟用功能,則會將其視為已停用。

例如,可以設計 Microsoft Edge 瀏覽器功能篩選。 只要 HTTP 要求來自 Microsoft Edge,此功能篩選就會啟用其所附加的任何功能。

功能旗標組態

Go 功能管理支援透過內建的功能旗標提供者 azappconfig 取用在 Azure 應用程式設定中定義的功能旗標,並透過 FeatureFlagProvider 介面作為擴充點,取用由其他提供者所定義的功能旗標。

import (
    "context"
    "log"

    "github.com/Azure/AppConfiguration-GoProvider/azureappconfiguration"
    "github.com/microsoft/Featuremanagement-Go/featuremanagement"
    "github.com/microsoft/Featuremanagement-Go/featuremanagement/providers/azappconfig"
)


// ... ...
// Load Azure App Configuration
appConfig, err := azureappconfiguration.Load(ctx, authOptions, options)
if err != nil {
    log.Fatalf("Failed to load configuration: %v", err)
}


// Create feature flag provider
featureFlagProvider, err := azappconfig.NewFeatureFlagProvider(appConfig)
if err != nil {
    log.Fatalf("Error creating feature flag provider: %v", err)
}

// Create feature manager
featureManager, err := featuremanagement.NewFeatureManager(featureFlagProvider, nil)
if err != nil {
    log.Fatalf("Error creating feature manager: %v", err)
}

功能旗標宣告

下列範例顯示用來在 JSON 檔案中設定功能旗標的格式。

{
    "feature_management": {
        "feature_flags": [
            {
                "id": "FeatureT",
                "enabled": true
            },
            {
                "id": "FeatureU",
                "enabled": false
            },
            {
                "id": "FeatureV",
                "enabled": true,
                "conditions": {
                    "client_filters": [
                        {
                            "name": "Microsoft.TimeWindow",
                            "parameters": {
                                "Start": "Wed, 01 May 2019 13:59:59 GMT",
                                "End": "Mon, 01 Jul 2019 00:00:00 GMT"
                            }
                        }
                    ]
                }
            }
        ]
    }
}

feature_management JSON 文件的區段會依慣例來載入功能旗標設定。 功能旗標物件必須列在 feature_flags 區段下的 feature_management 陣列中。

在上一節中,我們看到三個不同的功能。 功能旗標具有 idenabled 屬性。 id 是用來識別和參考功能旗標的名稱。 enabled 屬性會指定功能旗標的啟用狀態。 如果啟用為 false,則功能為 關閉 。 如果為true,則 enabled 特徵的狀態取決於條件。 如果沒有條件,則該功能為 開啟。 如果有條件,且符合條件,則功能會 開啟。 如果有條件且不符合條件,則該功能已 關閉。 conditions 屬性會宣告用來動態啟用功能的條件。 功能在 client_filters 陣列中定義其功能過濾器。 FeatureV 指定名為 Microsoft.TimeWindow 的功能篩選。 此篩選是可設定的功能篩選範例。 我們可以在範例中看到篩選條件具有 Parameters 屬性。 此屬性可用來設定篩選。 在此情況下,會設定要成為作用中功能的開始和結束時間。

您可以在feature_management找到 區段的詳細結構描述。

進階:功能旗標名稱中禁止使用冒號 ':'。

開/關聲明

下列程式碼片段示範了為簡單開/關功能定義功能的替代方法。

{
    "feature_management": {
        "feature_flags": [
            {
                "id": "FeatureT",
                "enabled": "true"
            },
            {
                "id": "FeatureX",
                "enabled": "false"
            }
        ]
    }
}

需求類型

功能旗標的 requirement_type 屬性可用來判斷篩選條件在評估功能的狀態時,是否應該使用 AnyAll 邏輯。 如果未指定 requirement_type,則預設值為 Any

  • Any 表示只有一個篩選條件需要評估為 true,即可啟用此功能。
  • All 表示每個篩選條件都必須評估為 true,才能啟用此功能。

requirement_typeAll 會變更周遊。 首先,如果沒有篩選條件,則會停用此功能。 然後,功能篩選會周遊,直到其中一個篩選條件決定應該停用功能為止。 如果沒有篩選條件表示應該停用功能,系統會將其視為已啟用。

{
    "feature_management": {
        "feature_flags": [
            {
                "id": "FeatureW",
                "enabled": "true",
                "conditions": {
                    "requirement_type": "All",
                    "client_filters": [
                        {
                            "name": "Microsoft.TimeWindow",
                            "parameters": {
                                "Start": "Wed, 01 May 2019 13:59:59 GMT",
                                "End": "Mon, 01 Jul 2019 00:00:00 GMT"
                            }
                        },
                        {
                            "name": "Percentage",
                            "parameters": {
                                "Value": "50"
                            }
                        }
                    ]
                }
            },
        ]
    }
}

在上述範例中,FeatureW 指定了 requirement_typeAll,這表示其所有篩選條件都必須評估為 true,才能啟用此功能。 在此情況下,此功能會在指定的時間範圍內針對 50% 的使用者啟用。

使用量

功能管理的基本形式是檢查功能旗標是否已啟用,然後根據結果執行動作。 檢查功能旗標的狀態會透過 FeatureManagerIsEnabled 方法來完成。

// Create feature flag provider
featureFlagProvider, err := azappconfig.NewFeatureFlagProvider(appConfig)
if err != nil {
    log.Fatalf("Error creating feature flag provider: %v", err)
}

// Create feature manager
featureManager, err := featuremanagement.NewFeatureManager(featureFlagProvider, nil)
if err != nil {
    log.Fatalf("Error creating feature manager: %v", err)
}

// Check if feature is enabled
enabled, err := featureManager.IsEnabled("FeatureX")
if err != nil {
    log.Printf("Error checking feature: %v", err)
    return
}

if enabled {
    // Do something
}

實作功能篩選

建立功能篩選可讓您根據您定義的準則來啟用功能。 若要實作功能篩選,必須實作 FeatureFilter 介面。 FeatureFilter 有一個名為 Evaluate的方法。 當功能與篩選器相關聯時,會在評估期間叫用該 Evaluate 方法。 如果傳回 Evaluate,則true會將此功能視為已啟用。

type FeatureFilter interface {
	// Name returns the identifier for this filter
	Name() string

	// Evaluate determines whether a feature should be enabled based on the provided contexts
	Evaluate(evalCtx FeatureFilterEvaluationContext, appCtx any) (bool, error)
}

type FeatureFilterEvaluationContext struct {
	// FeatureName is the name of the feature being evaluated
	FeatureName string

	// Parameters contains the filter-specific configuration parameters
	Parameters map[string]any
}

下列程式碼片段示範如何實作自訂功能篩選器。

type MyCustomFilter struct{}

func (f *MyCustomFilter) Evaluate(ctx context.Context, context *FeatureFilterEvaluationContext) bool {
    // Custom logic to determine if feature should be enabled
    if satisfyCriteria() {
        return true
    }
    return false
}

func (f *MyCustomFilter) Name() string {
    return "MyCustomFilter"
}

功能篩選的註冊方式是在建立 FeatureManager 時提供它們。 如果自訂功能篩選器需要任何上下文,可以透過FeatureFilterEvaluationContext參數傳遞。

// Register custom filters
options := &featuremanagement.Options{
    Filters: []featuremanagement.FeatureFilter{
        &MyCustomFilter{},
    },
}

// Create feature manager with custom filters
featureManager, err := featuremanagement.NewFeatureManager(featureFlagProvider, options)
if err != nil {
    log.Fatalf("Error creating feature manager: %v", err)
}

篩選別名屬性

為功能旗標註冊功能篩選器時,其名稱會用作預設別名。 您可以透過實作 Name() 方法來覆寫此識別碼,該方法會指定在功能旗標中參考篩選時在組態中使用的名稱。

缺少功能篩選器

如果功能設定為針對特定功能篩選啟用,且未註冊該功能篩選,則在評估該功能時會傳回錯誤。

內建功能篩選

featuremanagement 套件隨附兩個功能篩選:TimeWindowFilterTargetingFilter

每個內建功能篩選都有自己的參數。 以下是功能篩選清單以及範例。

Microsoft.TimeWindow

此篩選提供根據時間範圍啟用功能的功能。 如果只指定了 End,則會在該時間之前將功能視為開啟。 如果只指定了 Start,則會在該時間之後的所有時間點將功能視為開啟。

"client_filters": [
    {
        "name": "Microsoft.TimeWindow",
        "parameters": {
            "Start": "Wed, 01 May 2019 13:59:59 GMT",
            "End": "Mon, 01 Jul 2019 00:00:00 GMT"
        }
    }
]     

Microsoft.Targeting

此篩選提供為目標對象啟用功能的功能。 下列目標一節將提供目標設定的深入說明。 篩選參數包含 Audience 物件,描述使用者、群組、排除的使用者/群組,以及應該具有此功能存取權之使用者群的預設百分比。 Groups 區段中所列的每個群組物件也必須指定群組成員應具有存取權的百分比。 如果在 Exclusion 區段中指定了使用者,則無論直接或如果使用者位於排除的群組中,都會停用此功能。 否則,如果直接在 Users 區段中指定使用者,或使用者屬於任何群組推出的內含百分比,或如果使用者落入預設推出百分比,則該使用者就會啟用該功能。

"client_filters": [
    {
        "name": "Microsoft.Targeting",
        "parameters": {
            "Audience": {
                "Users": [
                    "Jeff",
                    "Alicia"
                ],
                "Groups": [
                    {
                        "Name": "Ring0",
                        "RolloutPercentage": 100
                    },
                    {
                        "Name": "Ring1",
                        "RolloutPercentage": 50
                    }
                ],
                "DefaultRolloutPercentage": 20,
                "Exclusion": {
                    "Users": [
                        "Ross"
                    ],
                    "Groups": [
                        "Ring2"
                    ]
                }
            }
        }
    }
]

Targeting

目標是功能管理策略,可讓開發人員逐漸向使用者群推出新功能。 此策略是建立在以一組稱為目標「對象」的使用者為目標的概念之上。 對象由特定使用者、群組、排除的使用者/群組和整個使用者群的指定百分比所組成。 在觀眾中所包含的群組,可以進一步按各群組成員的比例進行細分。

下列步驟示範新 'Beta' 功能的漸進式推出範例:

  1. 個別使用者 Jeff 和 Alicia 會被授與 Beta 的存取權。
  2. 另一位使用者 Mark 要求加入並包含在內。
  3. Beta 中包含 20% 稱為「Ring1」使用者的群組。
  4. Beta 中包含的「Ring1」使用者數目高達 100%。
  5. Beta 中包含 5% 的使用者群。
  6. 部署百分比增加到 100%,且功能已完全部署完成。

此推出功能的策略是透過內含的 Microsoft.Targeting 功能篩選內建在程式庫中。

將使用者設為目標

目標篩選條件依賴於目標內容來評估是否應該開啟功能。 此目標內容包含資訊,例如目前正在評估的使用者,以及使用者所在的群組。 呼叫 IsEnabledWithAppContext 時,必須直接傳遞目標內容。

// ... ...
// Create targeting context
targetingCtx := featuremanagement.TargetingContext{
    UserID: "test_user",
    Groups: []string{"Ring1"},
}

// Check if feature is enabled for the user
enabled, err := featureManager.IsEnabledWithAppContext("Beta", targetingCtx)
if err != nil {
    log.Printf("Error checking feature: %v", err)
    return
}

if enabled {
    // Feature is enabled for this user
}

目標排除

定義對象時,可以從對象中排除使用者和群組。 向一組使用者推出功能時,排除非常有用,但需要從推出中排除少數使用者或群組。 排除是藉由將使用者和群組清單新增至對象的 Exclusion 屬性來定義。

"Audience": {
    "Users": [
        "Jeff",
        "Alicia"
    ],
    "Groups": [
        {
            "Name": "Ring0",
            "RolloutPercentage": 100
        }
    ],
    "DefaultRolloutPercentage": 0,
    "Exclusion": {
        "Users": [
            "Mark"
        ]
    }
}

在上述範例中,此功能會針對名為 JeffAlicia 的使用者啟用。 它也會針對名為 Ring0 的群組中的使用者啟用。 不過,如果使用者命名為 Mark,則不論使用者是否在群組 Ring0 中,都會停用此功能。 排除項目優先於目標篩選的其餘部分。

變體

將新功能新增至應用程式時,總會發生功能有多個不同的建議設計選項的情況。 決定設計的常見解決方案是某種形式的 A/B 測試。 A/B 測試涉及向用戶群的不同部分提供不同版本的功能,並根據用戶互動選擇版本。 在此程式庫中,此功能是藉由代表具有變體之功能的不同組態來啟用。

變體可讓功能旗標變成不只是簡單的開啟/關閉旗標。 變體代表功能旗標的值,可以是字串、數位、布林值,甚至是組態物件。 宣告變體的功能旗標應該在應使用每個變體的情況下定義,這會在配置變體一節中更詳細地說明。

type Variant struct {
	// Name uniquely identifies this variant
	Name string

	// ConfigurationValue holds the value for this variant
	ConfigurationValue any
}

取得變體

針對每個功能,可以使用 FeatureManagerGetVariant 方法來擷取變體。 變體指派取決於目前正在評估的使用者,而該資訊為從您傳入的目標內容上所取得的。

targetingCtx := featuremanagement.TargetingContext{
    UserID: "Adam",
}

variant, err := featureManager.GetVariant("TestVariants", targetingCtx)
if err != nil {
    log.Printf("Error getting variant: %v", err)
    return
}

if variant != nil {
    variantConfiguration := variant.Configuration

    // Do something with the resulting variant and its configuration
}

變體功能旗標宣告

相較於一般功能旗標,變體功能旗標多了兩個屬性:variantsallocationvariants 屬性是一個陣列,其中包含為這項功能定義的變體。 allocation 屬性會定義應該如何為功能配置這些變體。 就像宣告一般功能旗標一樣,您可以在 JSON 檔案中設定變體功能旗標。 以下是變體功能旗標的範例。

{
    "feature_management": {
        "feature_flags": [
            {
                "id": "MyVariantFeatureFlag",
                "enabled": true,
                "allocation": {
                    "default_when_enabled": "Small",
                    "group": [
                        {
                            "variant": "Big",
                            "groups": [
                                "Ring1"
                            ]
                        }
                    ]
                },
                "variants": [
                    { 
                        "name": "Big"
                    },  
                    { 
                        "name": "Small"
                    } 
                ]
            }
        ]
    }
}

定義變體

每個變體都有兩個屬性:名稱和組態。 此名稱是用來參考特定變體,而組態是該變體的值。 您可以使用 configuration_value 屬性來設定組態。 configuration_value 是可以是字串、數位、布林值或組態物件的內嵌組態。 如果未指定 configuration_value,所傳回變體的 Configuration 屬性是 nil

所有可能變體的清單都會針對 variants 屬性下的每個功能定義。

{
    "feature_management": {
        "feature_flags": [
            {
                "id": "MyVariantFeatureFlag",
                "variants": [
                    { 
                        "name": "Big", 
                        "configuration_value": {
                            "Size": 500
                        }
                    },  
                    { 
                        "name": "Small", 
                        "configuration_value": {
                            "Size": 300
                        }
                    } 
                ]
            }
        ]
    }
}

配置變體

配置功能變體的程序取決於功能的 allocation 屬性。

"allocation": { 
    "default_when_enabled": "Small", 
    "default_when_disabled": "Small",  
    "user": [ 
        { 
            "variant": "Big", 
            "users": [ 
                "Marsha" 
            ] 
        } 
    ], 
    "group": [ 
        { 
            "variant": "Big", 
            "groups": [ 
                "Ring1" 
            ] 
        } 
    ],
    "percentile": [ 
        { 
            "variant": "Big", 
            "from": 0, 
            "to": 10 
        } 
    ], 
    "seed": "13973240" 
},
"variants": [
    { 
        "name": "Big", 
        "configuration_value": "500px"
    },  
    { 
        "name": "Small", 
        "configuration_value": "300px"
    } 
]

功能的 allocation 設定具有下列屬性:

房產 Description
default_when_disabled 指定當功能被視為停用且要求變體時,應該使用哪一個變體。
default_when_enabled 指定在功能視為已啟用時要求變體,且未將其他變體指派給使用者時,應該使用哪個變體。
user 指定變數和應為其指派該變體的使用者清單。
group 指定變體和群組清單。 如果使用者至少在其中一個群組內,則會指派變體。
percentile 指定變體和使用者計算百分比必須符合才能指派該變體的百分比範圍。
seed 計算 percentile 百分比依據的值。 如果使用相同的 seed 值,特定使用者的百分比計算在所有功能上都會相同。 如果未指定 seed,則會根據功能名稱建立預設種子。

如果未啟用此功能,功能管理員就會將標示為 default_when_disabled 的變體指派給目前使用者,在此案例中為 Small

如果啟用此功能,功能管理員將依該順序檢查 usergrouppercentile 配置,以指派變體。 在此特定範例中,如果評估的使用者名為 Marsha,則在名為 Ring1 的群組中,或使用者碰巧落在第 0 到 10 個百分位數之間,則會將指定的變異指派給使用者。 在此案例中,所有指派的使用者都會傳回 Big 變體。 如果這些配置都不相符,則會為使用者指派 default_when_enabled 變體,也就是 Small

配置邏輯與 Microsoft.Targeting 功能篩選類似,但目標中有一些參數不在配置中,反之亦然。 目標的結果與配置無關。

使用變體覆寫已啟用狀態

您可以使用變體來覆寫功能旗標的已啟用狀態。 覆寫可讓變體有機會延伸功能旗標的評估。 在具有變體的旗標上呼叫 IsEnabledWithAppContext 時,功能管理員會檢查指派至目前使用者的變體是否已設定為覆寫結果。 覆寫會使用選擇性變體屬性 status_override 來完成。 根據預設,這個屬性會設定為 None,這表示變數不會影響旗標是否被視為已啟用或停用。 將 status_override 設定為 Enabled 可讓變數在選擇時覆寫要啟用的旗標。 將 status_override 設定為 Disabled 可提供相反的功能,因此會在選擇變體時停用旗標。 無法覆寫具有 enabledfalse 狀態的功能。

如果您使用具有二進位變體的功能旗標,則 status_override 屬性會很有用。 其可讓您繼續在應用程式中使用類似於 IsEnabledaWithAppContext 的 API,同時受益於變體隨附的新功能,例如,百分位數配置和種子。

{
    "id": "MyVariantFeatureFlag",
    "enabled": true,
    "allocation": {
        "percentile": [
            {
                "variant": "On",
                "from": 10,
                "to": 20
            }
        ],
        "default_when_enabled":  "Off",
        "seed": "Enhanced-Feature-Group"
    },
    "variants": [
        {
            "name": "On"
        },
        {
            "name": "Off",
            "status_override": "Disabled"
        }
    ]
}

在上述範例中,一律會啟用功能。 如果目前使用者位於 10 到 20 的計算百分位數範圍中,則會傳回 On 變體。 否則會傳回 Off 變體,因為 status_override 等於 Disabled,而功能現在會被視為已停用。

後續步驟

若要了解如何在應用程式中使用功能旗標,請繼續進行下列快速入門。

若要了解如何使用功能篩選,請繼續進行下列教學課程。