4. Feature Flag 架构设计:Flag管理服务、SDK集成、本地缓存与远程同步、高可用设计
Feature Flag 架构是灰度发布系统的核心骨架。一个生产级的 Feature Flag 系统需要同时解决 集中管理、运行时决策、性能开销 和 故障容错 四个关键问题。本章从架构分层出发,逐一拆解 Flag 管理服务、SDK 集成、本地缓存与远程同步机制,以及高可用设计策略。
4.1 整体架构分层
Feature Flag 系统通常采用 控制面(Control Plane) 与 数据面(Data Plane) 分离的架构。控制面负责 Flag 的创建、配置、发布和审计;数据面负责在应用运行时高效获取 Flag 状态并做出决策。
架构核心原则: 控制面允许最终一致性(秒级延迟),数据面必须追求高可用与低延迟(毫秒级响应)。
4.2 Flag 管理服务
Flag 管理服务是控制面的核心组件,负责所有 Feature Flag 的 生命周期管理。它提供统一的 RESTful API 和 Web UI,供开发者和运维人员操作。
4.2.1 核心功能模块
| 功能模块 | 职责描述 | 关键技术选型 |
|---|---|---|
| Flag CRUD | 创建、更新、删除、查询 Flag 定义 | PostgreSQL / MySQL + 乐观锁 |
| 版本管理 | 每次变更生成新版本,支持回滚 | Event Sourcing + 快照 |
| 目标规则引擎 | 基于用户属性、百分比、环境等条件匹配 | 表达式引擎(如 Aviator / MVEL) |
| 审批与发布 | 变更需经过审批流,支持灰度发布策略 | 工作流引擎(如 Temporal / Camunda) |
| 审计日志 | 记录谁在什么时间做了什么变更 | Kafka + Elasticsearch |
4.2.2 数据模型设计要点
Flag 管理服务的数据模型需要兼顾 查询效率 和 历史追溯。推荐采用 主表 + 变更日志表 的双表设计:
- flag_definitions 表: 存储当前生效的 Flag 配置,包含 key、name、variations、rules、status 等字段。
- flag_changelog 表: 存储每次变更的完整快照,用于审计和回滚。
- flag_targeting_rules 表: 存储条件规则,支持 AND/OR 嵌套逻辑。
-- 简化版 Flag 定义表结构
CREATE TABLE flag_definitions (
id BIGSERIAL PRIMARY KEY,
project_id BIGINT NOT NULL,
key VARCHAR(128) NOT NULL UNIQUE,
name VARCHAR(256),
description TEXT,
variations JSONB NOT NULL, -- [{"id":"on","value":true}, {"id":"off","value":false}]
rules JSONB, -- 目标规则配置
status VARCHAR(20) DEFAULT 'inactive', -- inactive | active | archived
version INT NOT NULL DEFAULT 1,
created_by VARCHAR(64),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(project_id, key)
);
-- 变更日志表
CREATE TABLE flag_changelog (
id BIGSERIAL PRIMARY KEY,
flag_id BIGINT NOT NULL REFERENCES flag_definitions(id),
version INT NOT NULL,
snapshot JSONB NOT NULL, -- 变更时的完整 Flag 快照
operator VARCHAR(64),
operation VARCHAR(20), -- create | update | delete | rollback
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
4.3 SDK 集成与本地缓存
SDK 是数据面的核心组件,嵌入到业务应用中。它的职责是:从远程同步 Flag 配置,在本地缓存中决策,并返回 Flag 的最终值。
4.3.1 SDK 核心接口
| 接口方法 | 说明 | 典型实现 |
|---|---|---|
getFlag(key, userContext) |
根据用户上下文获取 Flag 值 | 先查本地缓存,命中直接返回 |
getFlagDetail(key) |
获取 Flag 的完整配置(含规则) | 用于调试和展示 |
getAllFlags() |
获取当前项目所有 Flag 的快照 | 用于批量评估或导出 |
onFlagChanged(callback) |
注册 Flag 变更回调 | WebSocket 推送时触发 |
4.3.2 本地缓存策略
本地缓存是 SDK 性能的关键。推荐采用 两级缓存 架构:
- 一级缓存(L1): 使用
ConcurrentHashMap或CopyOnWriteArrayMap,存储最近访问的 Flag 值。TTL 设置为 5~30 秒。 - 二级缓存(L2): 使用本地文件或嵌入式数据库(如 RocksDB),存储全量 Flag 配置。用于 SDK 重启后快速恢复,避免冷启动时大量请求穿透到远程服务。
最佳实践: 本地缓存不要设置过长的 TTL。Feature Flag 的变更通常需要秒级生效,建议 L1 缓存 TTL 不超过 30 秒,并配合远程推送机制实现即时更新。
4.4 远程同步机制
远程同步负责将控制面的 Flag 变更实时或准实时地推送到所有 SDK 实例。常见的同步方式有三种:
| 同步方式 | 延迟 | 服务端压力 | 适用场景 |
|---|---|---|---|
| 长轮询(Long Polling) | 秒级(通常 1~5s) | 中等 | 内网环境、连接数可控 |
| WebSocket 推送 | 毫秒级(实时) | 较低(事件驱动) | 公网环境、需要即时生效 |
| gRPC Stream | 毫秒级 | 低(双向流) | 微服务架构、高性能要求 |
生产环境推荐 WebSocket + 定期轮询 的双通道策略:WebSocket 负责实时推送,轮询作为兜底机制,防止 WebSocket 连接意外断开导致配置滞后。
4.5 高可用设计
Feature Flag 系统的高可用设计需要覆盖 控制面 和 数据面 两个层面。数据面的高可用优先级更高,因为 Flag 决策直接影响线上业务。
4.5.1 数据面高可用策略
- 本地缓存兜底: 当远程同步服务不可用时,SDK 继续使用本地缓存的 Flag 配置。即使缓存过期,也返回最后一次成功同步的值,而不是抛出异常。
- 默认值策略: 每个 Flag 在 SDK 中配置一个 静态默认值。当本地缓存为空且远程不可达时,返回默认值(通常为关闭状态)。
- 熔断与降级: SDK 对远程同步请求实施熔断机制。连续失败超过阈值(如 3 次)后,暂停重试 30 秒,避免雪崩。
- 多副本部署: 同步服务至少部署 2 个副本,通过负载均衡分发 SDK 请求。
4.5.2 控制面高可用策略
| 组件 | 高可用措施 | 故障影响 |
|---|---|---|
| Flag 管理 API | 无状态 + 水平扩展 + 数据库主从 | 控制面不可用,数据面不受影响 |
| 数据库 | 主从复制 + 自动故障转移(如 Patroni) | 写入中断,读取仍可用 |
| 消息队列 | Kafka 集群 + 副本机制 | 变更推送延迟,不丢失 |
| 缓存(Redis) | 哨兵模式或 Redis Cluster | 同步服务降级为轮询模式 |
4.5.3 故障场景与应对
关键故障场景: 假设同步服务完全宕机,所有 SDK 实例同时丢失远程连接。此时 SDK 应继续使用本地缓存,并定期尝试重连。业务应用不会感知到任何异常,Flag 状态停留在最后一次同步的值。这是 Feature Flag 系统最重要的 韧性设计。
下表总结了常见故障场景及系统行为:
| 故障场景 | 系统行为 | 对业务的影响 |
|---|---|---|
| 同步服务宕机 | SDK 使用本地缓存,定期重试 | 无影响,Flag 状态冻结 |
| 数据库主库故障 | 自动切换到从库,写入暂不可用 | 控制面无法变更 Flag,数据面正常 |
| 网络分区 | SDK 本地缓存 + 默认值兜底 | 无影响,分区恢复后自动同步 |
| SDK 内存溢出 | L2 缓存(本地文件)恢复数据 | 重启后快速恢复,冷启动延迟降低 |
4.6 架构设计总结
一个生产级的 Feature Flag 架构需要做到:
- 控制面与数据面分离: 控制面负责管理,数据面负责决策,两者通过同步服务解耦。
- 本地缓存优先: SDK 始终优先读取本地缓存,远程同步仅用于更新缓存。
- 多通道同步: WebSocket 实时推送 + 轮询兜底,确保配置及时生效。
- 韧性设计: 任何远程组件故障都不应导致业务应用崩溃,本地缓存和默认值是最后防线。
架构设计黄金法则: Feature Flag 系统的高可用不是让所有组件都不故障,而是确保在部分组件故障时,业务应用仍然可以正常运行并做出正确的 Flag 决策。