Feature Flag Starter
受众:开发者 摘要:Pennant-like 功能开关底座。支持配置式开关、数据库动态 Store、属性 gate、确定性百分比 rollout、variant、Facade 调用和 Admin 管理。
何时使用
| 场景 | 建议 |
|---|---|
| 新功能分批开放 | 配置 rollout-percentage,按用户或租户稳定分桶 |
| 付费 / 套餐能力可见性 | 使用 attributes 限定 plan、tenant、region 等上下文 |
| A/B 试验轻量分流 | 配置 variants 权重,业务读取当前 variant |
| 单元测试 / 隔离诊断中临时关闭能力 | 使用 spring.open.feature-flag.flags.<name>.enabled=false |
| 需要后台审计和动态变更 | 使用 spring-open-core-feature-flag,通过 Admin 功能开关页面维护数据库定义并刷新运行时 Store |
Feature Flag 是运行时能力开关,不替代 IAM 权限、套餐余额、风控规则或配置中心。权限判断仍走 IAM / Entitlement;开关只决定“当前代码路径是否启用”。轻量项目可以只使用 starter 的配置文件 Store;默认应用装配会引入 spring-open-core-feature-flag,数据库 Store 优先、配置文件 Store 兜底。
快速开始
<dependency>
<groupId>com.springopen</groupId>
<artifactId>spring-open-starter-feature-flag</artifactId>
</dependency>spring:
open:
feature-flag:
enabled: true
default-enabled: false
flags:
new-checkout:
enabled: true
rollout-percentage: 25
attributes:
plan: pro
variants:
blue: 70
green: 30FeatureFlagContext context = FeatureFlagContext.user(userId)
.withAttribute("plan", "pro");
if (FeatureFlags.enabled("new-checkout", context)) {
Optional<String> variant = FeatureFlags.variant("new-checkout", context);
// run new checkout
}机制说明
| 层级 | 职责 |
|---|---|
FeatureFlags | 静态 Facade,业务代码最常用入口 |
FeatureFlagManager | 合并默认值、属性 gate、rollout 和 variant |
FeatureFlagStore | 开关定义来源 SPI |
PropertiesFeatureFlagStore | 默认配置文件 store,不依赖数据库 |
FeatureFlagContext | 调用方传入 actor 和属性,starter 不绑定 IAM / Tenant |
评估顺序:
- 按名称读取
FeatureFlagStore。 - 未声明时返回
default-enabled。 - 已声明时先解析
enabled,为空则继承default-enabled。 attributes全部精确匹配后才进入 rollout。rollout-percentage通过flagName + actorType + actorId做 SHA-256 稳定分桶。- 开启后按
variants权重返回稳定 variant。
百分比 rollout 是确定性的:同一个 flag + actor 会长期命中同一个 bucket,适合灰度发布和回滚。匿名上下文使用 anonymous:anonymous,只适合全局开关或本地 smoke;用户、租户、应用等真实灰度应传入明确 actor。
配置项
spring:
open:
feature-flag:
enabled: true
default-enabled: false
flags:
feature-name:
enabled: true
description: New checkout flow
rollout-percentage: 100
attributes:
plan: pro
variants:
a: 50
b: 50| 配置 | 默认值 | 说明 |
|---|---|---|
enabled | true | 是否启用 starter 自动装配 |
default-enabled | false | 未声明开关或开关未显式配置 enabled 时的默认状态 |
flags.<name>.enabled | 空 | 显式开关状态;为空时继承 default-enabled |
flags.<name>.description | 空 | 面向文档、后台和排障的人类可读描述 |
flags.<name>.rollout-percentage | 100 | 开启后的百分比灰度,范围自动收敛到 0..100 |
flags.<name>.attributes | 空 | 必须匹配的上下文属性键值 |
flags.<name>.variants | 空 | variant 权重,非正数权重会被忽略 |
扩展 Store
业务项目可以提供自定义 FeatureFlagStore Bean,自动配置会让出默认配置文件 store。默认应用已提供数据库 Store:
@Bean
FeatureFlagStore databaseFeatureFlagStore(...) {
return new DatabaseFeatureFlagStore(...);
}自定义 store 只负责返回 FeatureFlagDefinition,不要把灰度算法和 Facade 状态复制到业务模块。需要后台管理、缓存刷新、权限菜单和数据库持久化时,优先使用 spring-open-core-feature-flag 管理面消费这个 SPI。
管理控制台
spring-open-core-feature-flag 提供后台管理 API 和 Admin 页面:
| 能力 | 说明 |
|---|---|
| 数据表 | feature_flag 存储开关编码、名称、描述、启停、灰度百分比、属性 gate、variant 权重、排序和系统托管标记 |
| 后台 API | /api/v1/admin/feature-flags/** 提供分页、详情、新增、更新、启停、批量删除、评估和刷新 Store |
| 权限 | feature-flag:flag:view/create/update/delete |
| 菜单 | Admin 内置 功能开关 / Feature Flags,默认归入系统管理域 |
| Store | DatabaseFeatureFlagStore 优先读取数据库定义;数据库不存在定义时回退 starter 配置文件定义 |
| 多节点刷新 | 默认本机刷新;启用 spring.open.feature-flag.cluster.enabled=true 且存在 Redisson 时,通过 Redis Topic 广播刷新事件 |
Admin 页面支持搜索、启停、灰度百分比、属性 JSON、variant JSON、手动评估和刷新 Store。属性 JSON 值按字符串精确匹配;variant JSON 值为权重数字。
多节点刷新
单机或 lite 场景下,后台保存功能开关后只刷新当前进程内的 DatabaseFeatureFlagStore 缓存。多实例部署如果需要节点间即时同步,可以开启 core 模块的 Redis Topic 刷新广播:
spring:
open:
feature-flag:
cluster:
enabled: true
topic: feature-flag.refresh
node-id: admin-node-1开启后,当前节点保存、启停、删除或手动刷新功能开关时会先刷新本机缓存,再向 Redis Topic 发布刷新消息;其他节点收到消息后只刷新本机缓存,不再二次广播,避免循环 fan-out。node-id 留空时会自动生成,生产多副本建议配置为稳定实例标识,便于排障。
审计
后台创建、更新、启停和删除 Feature Flag 时,会额外写入 Audit 操作日志短摘要。更新摘要只记录字段级差异,例如名称、启停状态、灰度比例、attributes、variants 和排序变化;JSON 字段只写入截断后的变化摘要,避免把大段配置塞入操作日志。审计失败不会阻断 Feature Flag 保存或刷新。
验证
./mvnw -pl spring-open-starters/spring-open-starter-feature-flag -am test -DskipITs -DskipFrontend -Dsurefire.failIfNoSpecifiedTests=false
./mvnw -pl spring-open-core/spring-open-core-feature-flag,spring-open-application -am test -DskipITs -DskipFrontend -Dsurefire.failIfNoSpecifiedTests=false
pnpm -C views/admin typecheck
git diff --check -- spring-open-starters/spring-open-starter-feature-flag spring-open-core/spring-open-core-feature-flag views/admin/src/views/feature-flag views/admin/src/api/feature-flag.ts docs/zh-CN/feature-flag.md