ABP VNext 实战指南:动态功能开关(Unleash & LaunchDarkly)🚀
✨ TL;DR
- 一次性链式注册,零改动接入 ABP VNext,支持 Unleash 与 LaunchDarkly 双平台 🏃♂️
- 自定义 Scoped
IFeatureFilter,多租户上下文无缝联动 🌐 - Scrutor 装饰模式本地缓存+初始化回退策略,确保高性能、高可用 💾🛡️
- 全链路示例:灰度/AB 测试、在线更新、运维 UI、审计、单元 & 集成测试 🛠️
1. 背景与动机 🎯
在传统 CI/CD 中,「部署即上线」,回滚成本高。功能开关(Feature Flags)让你:
- 小流量灰度发布,安全可控
- AB 测试分流,数据驱动
- 一键回滚,极速下线
- 动态在线更新,无需重启
借助 ABP VNext 模块化与 DI,仅需 1 分钟,即可落地 Unleash(开源)与 LaunchDarkly(商业)双平台接入,并在多租户场景中稳健运行。
2. 环境与依赖 🛠️
// appsettings.json(共享)
{
"Unleash": {
"Url": "https://unleash.{ENV}.mycompany.com/api",
"ApiKey": "${UNLEASH_API_KEY}"
},
"LaunchDarkly": {
"SdkKey": "${LD_SDK_KEY}"
}
}
// Program.cs:配置加载
var builder = WebApplication.CreateBuilder(args);
builder.Configuration
.AddJsonFile("appsettings.json", false, true)
.AddJsonFile($"appsettings.{
builder.Environment.EnvironmentName}.json", true, true)
.AddEnvironmentVariables(prefix: "MYAPP_"); // Kubernetes/CI 注入 MYAPP_UNLEASH__APIKEY 等
var appSettings = builder.Configuration;
-
平台:.NET 6 + ABP VNext 6.x
-
NuGet 包:
Volo.Abp.FeatureManagement、Volo.Abp.AspNetCore.FeatureManagementMicrosoft.FeatureManagement、Microsoft.FeatureManagement.AspNetCoreUnleash.Client.Extensions(v5+)LaunchDarkly.ServerSdkScrutor(装饰模式)Testcontainers(集成测试)
3. 模块声明与服务注册 📦
// MyAbpFeatureModule.cs
using Microsoft.FeatureManagement;
using Unleash;
using LaunchDarkly.Sdk.Server;
using Scrutor;
[DependsOn(
typeof(AbpFeatureManagementDomainModule),
typeof(AbpFeatureManagementApplicationModule),
typeof(AbpFeatureManagementHttpApiModule),
typeof(AbpAspNetCoreFeatureManagementModule),
typeof(AbpTenantManagementDomainModule) // 多租户支持
)]
public class MyAbpFeatureModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var cfg = context.Services.GetConfiguration();
// 1. 链式注册 ABP 原生 & 自定义 FeatureFilters
context.Services.AddFeatureManagement()
.AddFeatureFilter<PercentageFilter>()
.AddFeatureFilter<UnleashFeatureFilter>()
.AddFeatureFilter<LaunchDarklyFeatureFilter>();
// 2. 注册 Unleash 客户端(单例)+ Scoped Filter
context.Services.AddUnleash(options =>
{
options.AppName = "MyAbpApp";
options.UnleashAPI = new Uri(cfg["Unleash:Url"]);
options.CustomHttpHeaders["Authorization"] = cfg["Unleash:ApiKey"];
options.PollingInterval = TimeSpan.FromSeconds(15);
});
context.Services.AddScoped<UnleashFeatureFilter>();
// 3. 注册 LaunchDarkly 客户端(单例)+ Scoped Filter
context.Services.AddLaunchDarklyServer(new SdkOptions
{
SdkKey = cfg["LaunchDarkly:SdkKey"],
StartWaitTime = TimeSpan.FromSeconds(5)
});
context.Services.AddScoped<LaunchDarklyFeatureFilter>();
// 4. MVC + [FeatureGate]
context.Services.AddControllers()
.AddFeatureManagement();
// 5. 运维端 UI(可选)
context.Services.AddFeatureManagementUI();
// 6. 装饰 IFeatureManager,为 CachedFeatureManager
context.Services.Decorate<IFeatureManager, CachedFeatureManager>();
}
public override void OnApplicationInitialization(ApplicationInitializationContext ctx)
{
var lifetime = ctx.ServiceProvider.GetRequiredService<IHostApplicationLifetime>();
lifetime.ApplicationStopping.Register(() =>
{
// 优雅释放 SDK
var unleash = ctx.ServiceProvider.GetService<IUnleash>();
if (unleash is IUnleash u && u.IsInitialized) u.Dispose();
var ld = ctx.ServiceProvider.GetService<ILdClient>();
if (ld is ILdClient l && l.Initialized) l.Dispose();
});
}
}
// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Host.AddApp<MyAbpFeatureModule>();
var app = builder.Build();
app.InitializeApplication();
app.UseStaticFiles(); // 若启用了 UI 静态资源
app.UseRouting();
app.MapControllers();
app.MapFeatureManagementEndpoints(); // Flag 管理 UI
app.Run();
💡 Tips
AddEnvironmentVariables(prefix: "MYAPP_")支持在容器/CI 中注入。.Decorate<IFeatureManager, CachedFeatureManager>()保留原始实现,无递归。- 确保
UseStaticFiles()在UseRouting()之前配置,才能加载管理 UI 静态资源。

最低0.47元/天 解锁文章
1045

被折叠的 条评论
为什么被折叠?



