寫在前面
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