软件工程之特性管理-feature-management-day-two

Unleash 如何作为控制面板(Control Plane)与服务侧解耦

1
2
3
4
5
6
7
8
9
  [Unleash Server]
↑ ↑
[Admin 配置] [监控回报]
↓ ↓
┌───────────────┐
│ FeatureContext│ ← 用户属性、风险等级、时间窗口...
└───────┬───────┘

[业务服务] → [新/旧逻辑分支]

以下是针对IT场景的五种Feature Flag类型的设计模式及代码实现,结合金融行业特有的安全性、合规性和高可用性要求:

1. Release Flags(新功能渐进发布)​

​场景​​:手机银行APP新转账流程灰度发布
​设计模式​​:客户分群策略 + 交易金额阈值控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// Spring Boot + Unleash (银行核心系统)
@RestController
@RequestMapping("/transfer")
public class TransferController {

@PostMapping
public ResponseEntity<TransferResponse> executeTransfer(
@RequestBody TransferRequest request,
@RequestHeader("X-Customer-Tier") String customerTier) {

// 根据客户等级和金额阈值启用新流程(VIP客户且金额<50万)
boolean isNewFlow = unleash.isEnabled(
"new_transfer_flow",
Map.of(
"customerTier", customerTier,
"amount", request.getAmount().toString()
)
);

if (isNewFlow && request.getAmount().compareTo(new BigDecimal("500000")) < 0) {
return newTransferService.execute(request); // 新风控引擎
} else {
return legacyTransferService.execute(request); // 原有逻辑
}
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Unleash策略配置
{
"name": "new_transfer_flow",
"strategies": [
{
"name": "gradualRollout",
"parameters": {
"percentage": 20 // 首批开放20%VIP客户
}
},
{
"name": "remoteAddress",
"parameters": {
"IPs": "10.2.3.0/24" // 仅内部测试网络可用
}
}
]
}

2. Experiment Flags(A/B测试)​

​场景​​:信用卡还款页面UI优化实验
​设计模式​​:哈希客户号分桶 + 实时埋点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# Django + Unleash (银行前端系统)
from UnleashClient import UnleashClient

# 初始化Unleash客户端
unleash = UnleashClient(
url="https://unleash.yourbank.com/api",
app_name="credit_card_frontend",
environment="production",
custom_headers={"Authorization": "YOUR_API_KEY"}
)
unleash.initialize_client()

def credit_card_payment(request):
customer_no = request.user.customer_no
context = {
"userId": customer_no,
"properties": {
"risk_level": get_risk_level(customer_no), # 自定义上下文属性
"country": request.user.country_code
}
}

# 使用Unleash判断是否启用实验
if unleash.is_enabled("credit_card_ui_v3", context):
# 获取Variant(需要Unleash配置策略变体)
variant = unleash.get_variant("credit_card_ui_v3", context)

# 实时埋点(Unleash默认支持impression事件)
track_event(
event_type="UI_EXPERIMENT",
customer_no=customer_no,
experiment_variant=variant['name'], # variant结构: {'name': 'assisted', 'enabled': True}
page="credit_card_payment"
)

return render(f"payment_{variant['name']}.html")
else:
return render("payment_control.html") # 默认UI

unleash 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Unleash Admin控制台配置
{
"name": "credit_card_ui_v3",
"enabled": true,
"strategies": [
{
"name": "flexibleRollout",
"parameters": {
"rollout": 100, // 100%流量参与
"stickiness": "userId", // 按用户ID固定分桶
"groupId": "experiment" // 实验分组
}
},
{
"name": "userWithId",
"parameters": {
"userIds": "12345,67890" // 可指定白名单用户
}
}
],
"variants": [
{
"name": "control",
"weight": 50 // 权重50%
},
{
"name": "simplified",
"weight": 30 // 权重30%
},
{
"name": "assisted",
"weight": 20, // 权重20%
"payload": {
"type": "json",
"value": {"showTutor": true} // 可携带额外参数
}
}
]
}

​3. Kill Switches(紧急熔断)​

​场景​​:大额转账系统异常熔断
​设计模式​​:双通道触发 + 本地缓存强制更新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 初始化Unleash客户端(需提前配置)
import "github.com/Unleash/unleash-client-go/v3"

func init() {
unleash.Initialize(
unleash.WithUrl("https://unleash.yourbank.com/api"),
unleash.WithAppName("payment_system"),
unleash.WithCustomHeaders(http.Header{"Authorization": []string{"YOUR_API_KEY"}}),
)
}

func ProcessLargeTransfer(transfer *pb.TransferRequest) (*pb.TransferResponse, error) {
// 使用Unleash判断熔断状态
ctx := unleash.Context{
UserId: transfer.UserId,
Properties: map[string]string{
"country": transfer.CountryCode,
"amount": strconv.FormatInt(transfer.Amount, 10),
"risk_level": getRiskLevel(transfer.UserId),
},
}

if !unleash.IsEnabled("large_transfer_enabled", ctx) {
audit.LogEmergencyEvent(transfer, "unleash_killswitch_activated")
return nil, status.Error(codes.Unavailable, "服务临时维护")
}

// 正常处理逻辑...
}

unleash 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Unleash Admin控制台配置
{
"name": "large_transfer_enabled",
"enabled": true,
"strategies": [
{
"name": "gradualRollout",
"parameters": {
"rollout": 100, // 默认100%开启
"stickiness": "userId"
}
},
{
"name": "remoteAddress",
"parameters": {
"IPs": "10.2.3.0/24" // 仅允许内网访问
}
}
],
"variants": [],
"dependencies": []
}

4. Operational Flags(系统迁移)​

场景​:核心账户系统数据库迁移
设计模式​:影子流量对比 + 自动回退

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import io.getunleash.Unleash;
import io.getunleash.UnleashContext;
import java.util.Map;
import java.util.HashMap;

public class AccountService {
private final Unleash unleash;
private final AuditLogger auditLogger;

public AccountService(Unleash unleash, AuditLogger auditLogger) {
this.unleash = unleash;
this.auditLogger = auditLogger;
}

public Balance getBalance(String accountNo) throws BankSystemException {
// 构建Unleash上下文
UnleashContext context = UnleashContext.builder()
.addProperty("accountType", getAccountType(accountNo))
.addProperty("timeWindow",
java.time.LocalTime.now().getHour() < 6 ? "low-traffic" : "peak")
.addProperty("accountNo", accountNo) // 用于单账户回退
.build();

boolean useNewDB = unleash.isEnabled("account_db_migration", context);

try {
Balance balance = useNewDB ?
newDbQuery(accountNo) :
legacyDbQuery(accountNo);

// 影子流量比对
if (useNewDB) {
Balance legacyBalance = legacyDbQuery(accountNo);
auditLogger.compareResults("balance_query", balance, legacyBalance);
}

return balance;
} catch (Exception ex) {
// 单账户回退(通过Unleash的个性化配置)
Map<String, String> fallbackConfig = new HashMap<>();
fallbackConfig.put("accountNo", accountNo);
unleash.more().updateContext("account_db_migration",
"disableForAccount", fallbackConfig);

throw new BankSystemException("DB_OPERATION_FAILED", ex);
}
}

private Balance newDbQuery(String accountNo) {
// 新数据库查询实现
}

private Balance legacyDbQuery(String accountNo) {
// 旧数据库查询实现
}

private String getAccountType(String accountNo) {
// 获取账户类型逻辑
}
}

unleash 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
{
"version": 1,
"features": [
{
"name": "account_db_migration",
"description": "银行核心系统数据库迁移开关",
"enabled": true,
"strategies": [
{
"name": "gradualRollout",
"parameters": {
"rollout": "30",
"stickiness": "accountType",
"groupId": "migration"
}
},
{
"name": "flexibleRollout",
"parameters": {
"rollout": "100",
"stickiness": "timeWindow",
"groupId": "low-traffic"
}
},
{
"name": "userWithId",
"parameters": {
"userIds": "disabled_account_1,disabled_account_2"
}
}
],
"variants": [],
"dependencies": [],
"tags": [
{
"type": "banking",
"value": "core-system"
}
],
"impressionData": true
}
],
"segments": [
{
"id": 1,
"constraints": [
{
"contextName": "timeWindow",
"operator": "IN",
"values": ["low-traffic"],
"caseInsensitive": false
}
]
}
]
}

5. Permission Flags(权限控制)​

场景​:企业网银大额审批功能
设计模式​:RBAC + 实时权限同步

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import io.getunleash.Unleash;
import io.getunleash.UnleashContext;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/transfer")
public class TransferApprovalController {

private final Unleash unleash;
private final AuditLogger auditLogger;

public TransferApprovalController(Unleash unleash, AuditLogger auditLogger) {
this.unleash = unleash;
this.auditLogger = auditLogger;
}

@PostMapping("/approve-large")
public ResponseEntity<?> approveLargeTransfer(
@RequestBody TransferRequest request,
@RequestHeader("X-User-Info") String userInfo) {

// 解析用户信息
User user = parseUserInfo(userInfo);

// 构建Unleash上下文
UnleashContext context = UnleashContext.builder()
.userId(user.getId())
.addProperty("roles", String.join(",", user.getRoles()))
.addProperty("amount", String.valueOf(request.getAmount()))
.addProperty("branchCode", user.getBranchCode())
.addProperty("currency", request.getCurrency())
.build();

// 检查权限
boolean isApprovalAllowed = unleash.isEnabled("large_transfer_approval", context);

if (!isApprovalAllowed) {
auditLogger.logAccessViolation(user.getId(), "large_transfer_approval_denied", request);
return ResponseEntity.status(HttpStatus.FORBIDDEN)
.body(new ErrorResponse("超出审批权限", "INSUFFICIENT_PRIVILEGES"));
}

// 执行审批逻辑
return processApproval(request, user);
}

private User parseUserInfo(String userInfo) {
// 解析JWT或其它用户信息
}

private ResponseEntity<?> processApproval(TransferRequest request, User user) {
// 审批逻辑实现
}
}

unleash admin 配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
{
"version": 1,
"features": [
{
"name": "large_transfer_approval",
"description": "大额转账审批权限控制",
"enabled": true,
"strategies": [
{
"name": "userWithId",
"constraints": [
{
"contextName": "roles",
"operator": "STR_CONTAINS",
"values": ["branch_manager"],
"caseInsensitive": false
},
{
"contextName": "amount",
"operator": "NUM_LTE",
"values": ["1000000"]
}
]
},
{
"name": "default",
"constraints": [
{
"contextName": "branchCode",
"operator": "IN",
"values": ["SHANGHAI", "BEIJING"],
"caseInsensitive": true
},
{
"contextName": "amount",
"operator": "NUM_LTE",
"values": ["5000000"]
},
{
"contextName": "currency",
"operator": "IN",
"values": ["CNY", "USD"]
}
]
}
],
"variants": [],
"tags": [
{
"type": "security",
"value": "high"
}
],
"impressionData": true
}
]
}