寫在前面
Web服務開發過程中我們經常有這樣的需求:
某些功能我必須我修改了配置才啟用,比如新用戶注冊送券等;
某個功能需到特定的時間才啟用,過后就失效,比如春節活動等;
某些功能,我想先對10%的用戶開放,驗證沒問題后再逐步全量開放等;
這就是功能開關。
日常開發中功能開關我們一般是寫到配置文件里的,根據不同的配置,做不同的邏輯;但,其實.net core是對功能開關有官方支持的,但因為跟Azure集成比較好所以文檔不在.net core的文檔里面,而是在Azure的文檔這邊:
https://docs.microsoft.com/en-us/azure/azure-app-configuration/
Asp.net Core中集成
Asp.net Core的功能開關(Feature Flag)是直接僅根據配置文件方式使用,和集成Azure配置中心使用的;我們來看看區別;
本地配置文件方式
1、先安裝包
install-package?Microsoft.FeatureManagement.AspNetCore
2、注入服務(net6)
builder.Services.AddFeatureManagement();
3、配置文件寫幾個配置
appsettings.json
"FeatureManagement":?{?//簡單功能開關"Beta":?true,"v1":?true,"v2":?true,}
以上配置對應以下的枚舉:
public?enum?MyFeatureFlags{Beta,V1,V2,PercentageFlag,TimeWindowFlag,CustomFeatureFlag}
其實不用枚舉,直接用字符串也是可以的;
4、使用功能開關
1、創建一個FeatureFlagController;
注入服務:
private?readonly?IFeatureManager?_featureManager;public?FeatureFlagController(IFeatureManager?featureManager)
{_featureManager?=?featureManager;
}
簡單功能開關:
///?<summary>
///?當啟用beta版本的時候接口有效
///?</summary>
///?<returns></returns>
[FeatureGate(MyFeatureFlags.Beta)]
[HttpGet]
public?async?Task<IActionResult>?Beta()
{var?beta?=?await?_featureManager.IsEnabledAsync(nameof(MyFeatureFlags.Beta));if?(beta){//beta版本特有邏輯}return?Success("Beta",?beta);?//這里Success是封裝的,大家可以改成返回Ok()
}///?<summary>
///?當啟用v1版本的時候接口有效
///?</summary>
///?<returns></returns>
[FeatureGate(MyFeatureFlags.V1)]
[HttpGet]
public?async?Task<IActionResult>?V1()
{return?Success("V1");
}///?<summary>
///?當啟用v2版本的時候接口有效
///?</summary>
///?<returns></returns>
[FeatureGate(MyFeatureFlags.V2)]
[HttpGet]
public?async?Task<IActionResult>?V2()
{return?Success("V2");
}
由于我們上面的配置都是開啟的:
...
//簡單功能開關
"Beta":?true,
"v1":?true,
"v2":?true,
可以看到:

接口都可以訪問,我們把v1改成false試試:

馬上404了,這里清晰的看到,功能開關在多版本Api上線下某個版本時候確實方便;
基于過濾器的功能開關:
基于過濾器的功能開關是有一定邏輯的功能開關;
a、百分率功能開關
///?<summary>
///?啟用百分率的功能開關
///?</summary>
///?<returns></returns>
[FeatureGate(MyFeatureFlags.PercentageFlag)]
[HttpGet]
public?async?Task<IActionResult>?PercentageFlag()
{return?Success("PercentageFlag");
}
builder.Services.AddFeatureManagement().AddFeatureFilter<PercentageFilter>();
添加配置:
FeatureManagement節點下:
//百分率功能開關
"PercentageFlag":?{"EnabledFor":?[{"Name":?"Percentage","Parameters":?{"Value":?30}}]
},
這里的配置參數值代表30%的概率啟用次功能,我們試試:

分別是不啟用,和啟用狀態;
b、時間窗口功能開關
///?<summary>
///?啟用時間窗口的功能開關
///?</summary>
///?<returns></returns>
[FeatureGate(MyFeatureFlags.TimeWindowFlag)]
[HttpGet]
public?async?Task<IActionResult>?TimeWindowFlag()
{return?Success("TimeWindowFlag");
}
builder.Services.AddFeatureManagement().AddFeatureFilter<TimeWindowFilter>();
添加配置:
//時間窗口功能開關
"TimeWindowFlag":?{"EnabledFor":?[{"Name":?"TimeWindow","Parameters":?{"Start":?"2022/08/03?08:00:00?+00:00",?//這里是UTC時間"End":?"2022/08/03?09:00:00?+00:00"}}]
},
這個開關在2022年8月3日 下午16時(北京時間)和17時這個世界窗口內才啟用;對應的:
Start:就是生效時刻;
End:失效時刻;
c、自定義功能開關
ok,以上都是系統內置的功能開關,我們來根據自己需求創建一個自定義的;
需求:某個功能只有在客戶端是手機或者平板的情況下啟用,pc端不啟用;
創建一個自定義功能開關過濾器類CustomFeatureFilter
[FilterAlias("CustomFeature")]
public?class?CustomFeatureFilter?:?IFeatureFilter
{private?readonly?IHttpContextAccessor?_httpContextAccessor;public?CustomFeatureFilter(IHttpContextAccessor?httpContextAccessor){_httpContextAccessor?=?httpContextAccessor;}public?Task<bool>?EvaluateAsync(FeatureFilterEvaluationContext?context){//這里參數模擬平臺,實際業務會有實際業務的邏輯var?platform?=?_httpContextAccessor.HttpContext.Request.Query["platform"].ToString();var?allowPlatform?=?context.Parameters.Get<CustomFeatureFilterSettings>();return?Task.FromResult(allowPlatform.AllowedPlatforms.Any(c?=>?c?==?platform));}
}public?class?CustomFeatureFilterSettings
{public?string[]?AllowedPlatforms?{?get;?set;?}
}
///?<summary>
///?自定義功能開關
///?</summary>
///?<returns></returns>
[FeatureGate(MyFeatureFlags.CustomFeatureFlag)]
[HttpGet]
public?async?Task<IActionResult>?CustomFeatureFlag(string?platform)
{return?Success("CustomFeatureFlag");
}
builder.Services.AddFeatureManagement().AddFeatureFilter<CustomFeatureFilter>();
添加配置:
//自定義功能開關
"CustomFeatureFlag":?{"EnabledFor":?[{"Name":?"CustomFeature","Parameters":?{"AllowedPlatforms":?[?//這里配置運行啟用功能的平臺"phone","pad"//"pc"]}}]
}
我們來測測:

可以看到僅在設置運行的平臺下啟用,驗證ok;
集成Azure配置中心App Configuration方式
1、添加配置:
"ConnectionStrings":?{"AppConfig":?"<your?app?connection?string>"},
具體可參考我之前的文章:《微軟Azure配置中心 App Configuration (一):輕松集成到Asp.Net Core》
2、啟用Azure功能開關:
var?connectionString?=?builder.Configuration.GetConnectionString("AppConfig");builder.Host.ConfigureAppConfiguration((hostingContext,?config)?=>
{//配置不同功能config.AddAzureAppConfiguration(options?=>{啟用功能開關特性options.Connect(connectionString)//啟用功能開關特性.UseFeatureFlags(options?=>{options.CacheExpirationInterval?=?TimeSpan.FromSeconds(30);?//配置FeatureFlag緩存本地時間(默認就是30)});});
});
UseFeatureFlags啟用后,本地配置文件的方式失效;
builder.Services.AddAzureAppConfiguration();?//注入服務
3、在Azure 配置中心后臺配置好功能開關(代替本地配置文件)
基本功能開關配置
創建配置:

填寫配置信息:

OK,以上是基本的配置,配置好后可以直接在列表頁面勾選啟用配置與否:

基于過濾器的開關配置
百分率功能開關

時間窗口功能開關

這里的Start date,和Expiry date,就對應前面配置文件的Start,End;
自定義功能開關

這里的過濾器的**Name:**CustomFeature ,要跟代碼標簽 [FilterAlias("CustomFeature")]
一致;
AllowedPlatforms:也要跟代碼里面的配置一致,["phone","pad"]這個是數組的寫法;
總結
1、啟用集成到Azure的UseFeatureFlags后,本地配置文件的方式失效;
2、Azure這套功能開關還是有可選之處的,我尤其喜歡其接口標簽[FeatureGate];
另外定義了的標準配置項,與Azure配置中心無縫集成,香。
源碼
https://github.com/gebiWangshushu/Hei.Azure.Test
[參考]
https://docs.microsoft.com/en-us/azure/azure-app-configuration/overview