ABP VNext 实战指南:动态功能开关(Unleash & LaunchDarkly)

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.FeatureManagementVolo.Abp.AspNetCore.FeatureManagement
    • Microsoft.FeatureManagementMicrosoft.FeatureManagement.AspNetCore
    • Unleash.Client.Extensions (v5+)
    • LaunchDarkly.ServerSdk
    • Scrutor(装饰模式)
    • 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 静态资源。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kookoos

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值