在现代生产系统中,特性标志(Feature Flags)不是简单的开关——它们是实现敏捷性、安全性和实验能力的战略杠杆。但开发者常像"在地下室开关电灯"般粗暴实现它们。
本文通过 .NET 实现健壮、可扩展、可测试的特性标志架构:
✳️ Minimal APIs 🧱 策略模式 🧪 上下文标志解析 🧩 DI 驱动架构
若你曾说过"加个 if 语句就行",请立即阅读本文,避免应用陷入条件语句地狱!
我们常见到这种导致维护噩梦的混乱模式,在 Minimal API 中尤为刺眼:
app.MapGet("/checkout", async (HttpContext ctx) => {
var flagService = ctx.RequestServices.GetRequiredService<IFeatureFlagProvider>();
if (await flagService.IsEnabledAsync("UseNewCheckout"))
return Results.Ok("Using new checkout!");
else
return Results.Ok("Using legacy checkout.");
});
❌ 致命缺陷:
HttpContext
| 原则 | 解决痛点 | 关键收益 | |---------------------|--------------------------|--------------------------| | 🔁 行为解耦 | 标志条件侵入业务逻辑 | 核心逻辑纯净可读 | | 🧱 策略模式 | 硬编码分支 | 动态注入实现版本切换 | | 🌍 上下文感知 | 全局开关缺乏精细控制 | 租户/环境/用户级灰度发布 | | 🧪 易于测试 | 模拟复杂依赖链 | 秒级单元测试 | | ⚙️ 组合式解析逻辑 | 复杂规则难以实现 | 多条件智能策略 | | 🧯 故障安全默认值 | 标志服务宕机导致系统崩溃 | 自动降级保核心功能 |
业务需求:
graph LR
A[仅租户 abc 启用] --> B[实时采集用户数据]
B --> C[零部署秒级回滚]
通过策略模式架构实现:精准控制 + 实时洞察 + 安全回滚
public interface IFeatureFlagProvider
{
/// <summary>
/// 基于上下文判断特性是否启用
/// </summary>
Task<bool> IsEnabledAsync(string flagName, FlagContext context);
}
public class FlagContext
{
public string? TenantId { get; set; } // 多租户支持
public string? Environment { get; set; } // 环境隔离
// 可扩展:用户ID/国家代码/浏览器类型等
}
public interface ICheckoutStrategy
{
Task<IResult> ProcessCheckoutAsync();
}
public class NewCheckoutStrategy : ICheckoutStrategy
{
public Task<IResult> ProcessCheckoutAsync() =>
Task.FromResult(Results.Ok("🚀 新结算系统处理"));
}
public class LegacyCheckoutStrategy : ICheckoutStrategy
{
public Task<IResult> ProcessCheckoutAsync() =>
Task.FromResult(Results.Ok("🏷️ 旧结算系统处理"));
}
public class CheckoutStrategyFactory
{
private readonly IServiceProvider _provider;
private readonly IFeatureFlagProvider _flagProvider;
private readonly ILogger _logger;
public async Task<ICheckoutStrategy> ResolveAsync(FlagContext context)
{
try
{
bool useNew = await _flagProvider.IsEnabledAsync("UseNewCheckout", context);
return useNew
? _provider.GetService<NewCheckoutStrategy>()
: _provider.GetService<LegacyCheckoutStrategy>();
}
catch (Exception ex) // 故障安全降级
{
_logger.LogError(ex, "标志解析失败,降级至旧系统");
return _provider.GetService<LegacyCheckoutStrategy>();
}
}
}
// Program.cs
builder.Services.AddSingleton<NewCheckoutStrategy>();
builder.Services.AddSingleton<LegacyCheckoutStrategy>();
builder.Services.AddSingleton<CheckoutStrategyFactory>();
app.MapPost("/checkout", async (HttpContext ctx, CheckoutStrategyFactory factory, IWebHostEnvironment env) =>
{
var context = new FlagContext {
TenantId = ctx.Request.Headers["X-Tenant-Id"],
Environment = env.EnvironmentName
};
var strategy = await factory.ResolveAsync(context);
return await strategy.ProcessCheckoutAsync();
});
public class FakeFlagProvider : IFeatureFlagProvider
{
private readonly Dictionary<string, bool> _flags = new();
public Task<bool> IsEnabledAsync(string featureName, FlagContext context)
=> Task.FromResult(_flags[featureName]);
}
[Fact]
public async Task 新标志开启时使用新系统()
{
// 构造测试环境
var services = new ServiceCollection()
.AddSingleton<NewCheckoutStrategy>()
.AddSingleton<LegacyCheckoutStrategy>()
.BuildServiceProvider();
// 模拟标志状态
var fakeFlags = new FakeFlagProvider("UseNewCheckout", true);
// 执行解析
var factory = new CheckoutStrategyFactory(services, fakeFlags, NullLogger.Instance);
var strategy = await factory.ResolveAsync(new FlagContext());
// 验证结果
var result = await strategy.ProcessCheckoutAsync();
Assert.Equal("🚀 新结算系统处理", ((Ok<string>)result).Value);
}
| 方案 | 适用场景 | 特点 | |-----------------------|------------------------|--------------------------| | LaunchDarkly | 企业级复杂灰度 | 实时定位/实验分析 | | Azure App Configuration | Azure 生态集成 | 动态更新/零代码部署 | | Redis 存储方案 | 高性能分布式系统 | 亚毫秒级标志更新 | | 数据库方案 | 全自定义需求 | 完全控制审计流 |
app.Use(async (ctx, next) =>
{
var flagProvider = ctx.RequestServices.GetService<IFeatureFlagProvider>();
var context = new FlagContext {
TenantId = ctx.Request.Headers["X-Tenant-Id"],
Environment = env.EnvironmentName
};
if (!await flagProvider.IsEnabledAsync("BetaAccess", context))
{
ctx.Response.StatusCode = 403;
await ctx.Response.WriteAsync("⛔ 您无权访问Beta功能");
return;
}
await next();
});
| 能力 | 业务价值 | |---------------------|--------------------------| | 🧪 A/B 测试 | 数据驱动功能迭代 | | 🏢 多租户精准控制 | SaaS 客户定制体验 | | ☁️ 云原生动态更新 | 无需重启修改功能 | | 🧩 模块化插件管理 | 运行时热插拔组件 | | ⛔ 秒级熔断 | 生产事故分钟级恢复 |
某 SaaS 团队实践成果:
- ✅ PR 杂项减少 60%
- ✅ 功能发布时间从小时级降至分钟级
- ✅ 关键回滚实现零停机
终极洞见: 特性标志架构不是开关管理,而是业务敏捷性的核心引擎!