复杂业务规则太多?我们搭了一个规则可视化配置平台

复杂业务规则太多?我们搭了一个规则可视化配置平台

一、为什么需要这样一个平台

在业务系统里,变化频繁且复杂度容易失控的部分,往往不是底层能力,而是各种业务规则。

比如是否允许开票、是否允许解绑、地址编辑是否允许、业务开关如何选择、风控命中策略如何调整等。这些规则通常有几个共同特点:

  • 判断条件多,容易随着城市、车型、业务线、渠道、订单状态等维度持续变化。

  • 规则变更频繁,但直接改代码发版周期长,直接改配置中心又缺少结构化约束。

  • 变更前难验证,变更后难对比,出了问题难追溯、难回滚。

  • 规则责任人、审批人、影响范围和发布过程缺少统一的平台化管控。

过去很多复杂业务逻辑会沉淀成代码分支或配置中心条目。代码分支稳定但灵活性不足,配置中心变更灵活,但在规则表达、验证和变更管控方面能力有限。

规则可视化配置平台要解决的核心问题,就是把"散落在代码和配置里的业务判断逻辑"抽象成可视化、可验证、可审批、可灰度、可追溯的策略资产。

典型问题

问题 影响 平台化后的解决方式
配置变更未通知责任人 相关业务方无法提前感知风险 发布审批、负责人通知、发布卡片
配置结构自由,容易写错 线上规则不符合预期,可能引发资损或舆情 可视化编排、字段类型约束、测试用例验证
回归成本高 需要构造真实业务链路才能验证 页面直接构造入参,离线执行规则链
上线缺少小流量验证 一次性全量生效风险高 双跑对比、按比例灰度切流
变更不可追溯 问题定位和回滚困难 快照、发布记录、审批记录、回滚能力

二、它是什么

规则可视化配置平台是一套面向多业务域的"规则配置、策略编排、发布治理和运行时执行平台"。

它把原本写在代码里的 if/elseswitch、策略选择、结果输出等逻辑,拆成可复用的字段、组件和链路。业务人员或研发人员可以在管理端通过可视化方式编排规则,平台再把规则发布成机器可执行的规则链,下发到业务服务运行时执行。

可以从四个层面理解:

  • 字段/选项:规则里可以使用的业务变量,例如订单字段、运单字段、账单字段、业务入参和中间结果。

  • 组件:一段独立业务判断或结果输出,类似代码里的一个方法。

  • 链路:多个组件按照 EL 表达式编排成的完整规则逻辑,类似一段由多个方法组合出来的业务流程。

  • 运行时:业务服务引入 SDK 后,根据链路 ID 和入参执行规则,得到结构化结果。

它不是单纯的配置页面,也不是一个所有请求都要远程调用的规则服务。更准确地说,它由四层能力组成:

层次 角色 职责
管理层 规则管理端 规则空间、字段、组件、链路、测试用例、审批、发布、对比、切流、回滚
配置服务层 配置中心服务端 规则配置持久化、发布到配置中心、提供批量查询
配置消费层 配置消费 SDK 业务服务启动拉取配置、本地缓存、监听变更、通知运行时
运行时层 规则执行 SDK 解析规则链、注册执行模型、热更新、执行规则、支持对比和灰度

整体主链路是:

Plaintext 复制代码
管理端配置规则
  -> 审批通过后发布
  -> 配置服务端落库并推送配置变更
  -> 配置消费 SDK 在业务服务内拉取和监听配置
  -> 规则执行 SDK 加载规则链
  -> 业务服务调用 SDK 执行规则

有了这个基本定义之后,再回头看最初的问题,就能更清楚地看到平台的价值:它不是简单为配置增加一个管理页面,而是补齐复杂规则在表达、验证、发布和运行时治理上的缺口。

三、它解决了什么问题

1. 从"配置字符串"升级为"结构化规则"

配置中心更适合存储简单配置,但复杂策略往往需要条件、分支、结果、嵌套链路和字段类型。规则可视化配置平台把这些内容抽象成空间、类别、选项、组件、链路和测试用例,让业务策略具备结构化表达能力。

能力 普通配置中心 规则可视化配置平台
表达方式 字符串 / JSON 字段 + 组件 + 链路 + EL
复杂分支 依赖人工维护 JSON 可视化编排
变更验证 通常依赖业务回归 页面测试用例直接执行
发布管控 基础审批和通知 负责人审批、变更明细、发布快照
灰度能力 依赖业务自行实现 双跑对比、按比例切流
回滚追溯 依赖配置历史 链路快照、发布快照、回滚
领域隔离 namespace 维度较粗 空间、权限组、业务类别

2. 降低业务变更成本

对于频繁调整的业务策略,不再要求每次都修改代码、发版、等待上线。规则维护人员可以在页面上调整条件和输出结果,研发主要负责沉淀可复用字段、组件和运行时能力。

3. 降低线上风险

规则发布前要求测试用例验证,发布中经过审批,发布后可以先双跑观察差异,再通过比例控制小流量切流。出现问题时,可以基于发布快照快速回滚。

4. 提升规则复用和沉淀

订单、运单、账单等业务字段可以统一维护,组件可以复用,链路可以引用子链路。随着落地场景增加,平台会逐步沉淀为跨业务域的策略资产库。

这些收益背后,对应的是一组必须具备的基础能力。也就是说,仅有"可视化配置"不足以支撑完整的规则治理,平台还需要把验证、审批、灰度、回滚和资产复用一起纳入设计。

四、平台需要具备哪些核心能力

如果一个规则平台只解决"把规则放到页面上配置",价值其实有限。更核心的问题是:规则如何表达、如何验证、如何发布、如何在线上安全生效,以及出问题时如何快速定位和回滚。

因此平台核心能力可以拆成几类:

1. 可视化编排

平台把规则拆成字段、组件和链路。字段负责描述规则可以使用的数据,组件负责承载独立判断或输出逻辑,链路负责把多个组件按顺序、条件、分支组合起来。

这样一来,复杂规则不再是一段难以理解的 JSON 或一堆散落的配置项,而是变成了可以阅读、复用和审查的结构化流程。

2. 测试用例验证

规则上线前必须能被验证。平台支持直接在页面构造入参并执行规则链,不需要完整走一遍真实业务接口。对于复杂命中条件,测试用例可以沉淀为规则资产的一部分,后续每次变更都可以复用。

3. 发布审批和变更追溯

规则虽然是配置,但影响不亚于代码。平台在发布前保留审批流程,审批时展示变更明细;发布后保留链路快照和发布快照。这样既能控制变更风险,也能在问题发生后快速定位"谁在什么时候改了什么"。

4. 双跑对比和灰度切流

对于高风险规则,平台支持新旧规则双跑。双跑阶段可以同时执行线上主链和待发布链路,只观察结果差异,不影响真实业务结果。确认差异符合预期后,再通过比例控制逐步把流量切到新规则。

5. 快速回滚

每次发布都会生成发布快照。出现问题时,可以基于历史快照恢复上一版本,并重新同步到配置中心。相比人工回滚配置,快照化回滚更可靠,也更适合复杂链路。

6. 字段和组件资产化

随着接入场景变多,业务字段、判断组件、结果组件会逐步沉淀下来。后续配置新规则时,不必每次从零开始,只需要复用已有字段和组件,再编排新的链路。

能力清单说明了平台要解决的问题,但真正落地时,还需要进一步回答"这些能力如何拆分、如何部署、如何运行"。这就进入到平台的核心设计。

五、核心设计思路

这一部分先不展开代码细节,而是先看几个关键设计取舍。它们决定了平台后续是否能够支撑复杂规则、频繁变更、线上灰度和本地高性能执行。

1. 管理面和运行面分离

规则可视化配置平台没有把所有能力做成一个大服务,而是拆成管理面、配置面和运行面:

  • 管理面:负责规则资产管理、测试用例、审批、发布、回滚和权限控制。

  • 配置面:负责规则配置的持久化、版本管理、配置推送、客户端拉取、本地缓存和变更通知。

  • 运行面:以 SDK 的方式嵌入业务服务本地执行规则,不需要每次执行都远程调用规则中心。

这样设计的好处是:规则执行链路短、性能稳定;规则发布链路又能复用现有配置中心和审批体系。

2. 以 Chain JSON 作为规则发布产物

管理端保存的是编辑态模型,包括 rule_chainrule_componentrule_chain_snapshotrule_chain_publish_snapshot 等。发布时,管理端把链路、组件、EL、参数类型、灰度比例等信息组装成 SDK 可识别的 Chain JSON。

发布到配置中心时使用固定约定:

Plaintext 复制代码
module = rules
key = chainKey
value = Chain JSON

业务服务只需要配置自己关心的 chainKeys,运行时 SDK 会自动从配置中心拉取对应规则。

3. 以 LiteFlow 作为执行内核

规则可视化配置平台的运行时没有重复造一套流程执行器,而是基于 LiteFlow 完成链路执行。SDK 负责把管理端发布的 ChainComponent 转换成 LiteFlow 可以识别的 Chain、EL 和 Script Node。

运行时核心对象包括:

  • ChainHelper:负责规则链和脚本组件的加载、重载、卸载和本地缓存。

  • RuleConfigParser:负责从配置缓存读取规则、监听变更,并触发热更新。

  • ChainExecutor:业务执行入口,负责主链执行、对比链双跑、按比例切流和结果封装。

  • ChainContext:规则执行上下文,提供当前链路、入参、中间计算结果等。

  • ChainResultContext:规则结果上下文,脚本组件通过它写入最终输出。

4. 发布前验证、发布中审批、发布后灰度

规则变更风险主要来自"配置正确性"和"线上影响范围"。因此设计上把风险控制拆成三个阶段:

阶段 控制手段 目的
发布前 测试用例、脚本校验、链路执行校验 确认规则逻辑符合预期
发布中 负责人审批、变更明细、权限控制 确认变更被正确的人审核
发布后 双跑对比、按比例切流、发布通知、回滚 小流量验证,发现问题快速止损

5. 支持热更新但保留兜底同步

规则配置发布后,配置服务端会写入数据库并推送配置变更。业务服务内的配置消费 SDK 一方面监听配置中心变更,另一方面也会定时拉取配置兜底,避免只依赖推送导致配置不一致。

热更新路径如下:

Plaintext 复制代码
配置中心变更
  -> 配置监听器
  -> 本地配置仓储更新缓存
  -> 发送变更事件
  -> 规则解析器监听到变化
  -> ChainHelper reload / unload
  -> LiteFlow FlowBus 更新规则链

上面讲的是设计取舍,接下来再往下一层,看这些设计在系统内部是如何协作的:规则如何从管理端流到运行时,模型如何转换,最终又如何通过 LiteFlow 执行。

六、核心原理

前面讲的是平台为什么要做,以及它提供了哪些能力。进入实现层面后,核心问题可以拆成三个:

  • 规则配置如何在各模块之间流转:分别由哪些模块负责编辑、发布、缓存和执行。

  • 规则在系统里如何被表达:页面上的空间、选项、组件、链路,最终会对应哪些系统模型。

  • 规则如何被真正执行:配置化模型如何转换成可执行流程,业务请求进入后如何得到结果。

这一节按「架构与模型 → 发布与治理 → 拉取、编译与执行」三条主线展开,顺序贴近一条规则从配置到上线的真实路径。

1. 整体架构

整体架构图关注的是"规则如何流转"。一条规则从页面配置开始,经过审批、发布、配置中心下发、客户端缓存,最后进入业务服务本地运行时执行。核心设计取舍是:管理和发布集中化,执行本地化

2. 模型关系

模型关系图关注的是"规则如何被表达"。管理端保存编辑态模型;发布时组装成 Chain JSON 写入配置中心;业务服务拉取后反序列化为运行时 Chain、Component,再交给执行器处理。

3. 规则发布全流程

规则发布是管理面到运行面的关键桥梁:ChainDTOChain JSON → RPC 写入配置服务端 → Apollo 推送到各业务 Pod。

3.1 链路状态

3.2 发布逻辑

4. 发布审批流程

5. 双跑对比与灰度切流

平台约定 {chainKey}Compare 作为影子链路 Key,在不影响主链结果的前提下验证新规则。

5.1 管理端发布逻辑

5.2 运行时执行分支

流程与代码对照(运行时)

流程步骤 关键入口
execute chainKey ChainExecutor.execute
加载 chainKeyCompare ChainHelper.getByChainCode(appendCompareSuffix(key))
版本不同且 flowPercent 为空 → 双跑 needComparedoExecute(main) + compare(...)
版本不同且命中比例 → 切流 needFlow + randomPercent <= flowPercent
版本相同 → 正常执行 doExecute(main)
JsonDiff 记录差异 compare()JsonDiff.asJson(old, new)

关键代码(规则引擎 SDK · ChainExecutor)

Java 复制代码
// executeAndComparedAndFlow --- 对应流程图三条分支
Chain chainCompare = ChainHelper.getByChainCode(appendCompareSuffix(chain.getKey()));

if (needCompare(chain, chainCompare)) {          // 版本不同 && flowPercent == null
    ChainExecuteResult result = doExecute(chain, params);
    compare(chain, result, chainCompare, params); // 影子执行 + JsonDiff,结果仍以主链为准
    return result;
}
if (needFlow(chain, chainCompare) && randomPercent <= chainCompare.getFlowPercent()) {
    return doExecute(chainCompare, params);      // 切流走新链
}
return doExecute(chain, params);                 // 正常执行

// needCompare / needFlow 判断条件
boolean needCompare = !main.version.equals(compare.version) && compare.flowPercent == null;
boolean needFlow    = !main.version.equals(compare.version) && compare.flowPercent != null;
阶段 状态 Compare 链 flowPercent 运行时行为
对比 COMPARE null 主链返回;影子执行 Compare 并 JsonDiff
切流 FLOW 1~100 按比例走 Compare 链
全量 PUBLISHED null(同版本) 不再对比/切流

6. 业务侧规则配置拉取

各业务服务引入配置消费 SDK 后,通过三条路径保持配置一致。

流程与代码对照

流程步骤 模块 关键入口
注册 chainKey + Compare 规则引擎 SDK LiteflowPropertyAutoConfiguration.parseKeys
服务启动 → RPC 拉取 配置消费 SDK TradeConfigStarter.startTradeConfigRepository.sync(STARTER)
batchFetchConfig RPC 配置消费 SDK TradeConfigRepository.syncTradeConfigClient.batchFetchConfig
releaseVersion fallback 配置服务端 TradeConfigService.getTradeConfig
Apollo 推送 → 解析变更 配置消费 SDK ApolloListener.listenneedUpdateupdateConfig
定时兜底同步 配置消费 SDK TradeConfigSyncSchedulesync(SCHEDULE)
写入本地缓存 配置消费 SDK fireConfigChangeTradeConfig.putValue
变更通知 → 热加载 规则引擎 SDK TradeConfigNotifyApolloParseHelper.listenChainHelper.reload

关键代码

Java 复制代码
// [启动] TradeConfigStarter --- 对应「服务启动」触发源
@PostConstruct
public void start() {
    tradeConfigRepository.sync(SyncSource.STARTER);   // RPC 拉取
    apolloListener.listen();                          // Apollo 监听
    tradeConfigSyncSchedule.start();                  // 定时兜底
}

// [启动/定时] TradeConfigRepository.sync --- 对应「batchFetchConfig RPC」
Lists.partition(keys, 10).forEach(batch -> {
    condition.setReleaseVersion(ReleaseVersionUtil.getReleaseVersion());
    tradeConfigFacade.batchFetchConfig(condition);    // → 配置服务端 RPC
});
fireConfigChange(key, oldValue, newValue, syncSource); // → 写缓存 + notify

// [Apollo] ApolloListener --- 对应「Apollo 推送」分支
config.addChangeListener(event -> {
    if (!needUpdate(tradeConfigKey, apolloKey)) return;  // 灰度版本路由
    tradeConfigRepository.updateConfig(key, apolloValue);
});

// [注册] 规则引擎 Starter --- 每个 chainKey 同时注册 Compare 链
keys.add(chainKey);
keys.add(appendCompareSuffix(chainKey));

// [fallback] 配置服务端 --- 灰度版本查不到时回退 default
if (tradeConfig == null && !DEFAULT.equals(releaseVersion)) {
    return getTradeConfigWithReleaseVersion(list, key, DEFAULT);
}

7. 规则编译与热加载

流程与代码对照

流程步骤 模块 关键入口
读取 Chain JSON 规则引擎 SDK TradeConfig.getProperty(MODULE, chainKey)
parseChain + checkChain 规则引擎 SDK ChainHelper.parseChain
注册 Script Node 规则引擎 SDK ChainHelper.loadChainScriptLiteFlowNodeBuilder
注册 Chain EL 规则引擎 SDK ChainHelper.loadChainLiteFlowChainELBuilder
配置 UPDATE → 热加载 规则引擎 SDK ApolloParseHelper.listenChainHelper.reload
配置 DELETE → 卸载 规则引擎 SDK ChainHelper.unload(引用计数归零)

关键代码(规则引擎 SDK)

Java 复制代码
// [启动] ApolloParseHelper.initFromApollo --- 对应「读取 → 解析 → 注册」
for (RuleEngineKey key : keys) {
    String chainJson = TradeConfig.getProperty(MODULE, key.getChainKey());
    Chain chain = ChainHelper.parseChain(chainJson);  // parseChain + checkChain
    ChainHelper.load(chain);
}

// [load] ChainHelper --- 对应「注册 Script Node + Chain EL」
public static void load(Chain chain) {
    loadChainScript(chain, chain);   // LiteFlowNodeBuilder.createScriptNode().build()
    loadChain(chain, chain);         // LiteFlowChainELBuilder.createChain().setEL(...).build()
}

// [热更新] ApolloParseHelper.listen --- 对应 fork 两条路径
TradeConfig.addListener(event -> {
    switch (changeType) {
        case ADD, UPDATE -> ChainHelper.reload(newChain);  // unload 旧链 → load 新链
        case DELETE      -> ChainHelper.unload(chain);      // 引用计数归零后卸载
    }
});

// [reload] ChainHelper.reload
if (oldChain != null) unload(oldChain);
load(chain);

8. 规则执行原理

流程与代码对照

流程步骤 模块 关键入口
ChainExecutor.execute 规则引擎 SDK ChainExecutor.execute(chainId, params)
对比 / 切流 / 正常路由 规则引擎 SDK executeAndComparedAndFlow
内存版本 vs 缓存版本兜底 规则引擎 SDK tryReloadChain
获取链路锁 规则引擎 SDK chain.getLock().tryLock()
构造上下文 规则引擎 SDK new ChainContext() + new ChainResultContext()
LiteFlow 执行 规则引擎 SDK flowExecutor.execute2Resp(chain.getCode(), params, contexts)
封装结果 规则引擎 SDK chainResultContext.getData()ChainExecuteResult

关键代码(规则引擎 SDK · ChainExecutor)

Java 复制代码
// execute --- 入口,先走路由再执行
public ChainExecuteResult execute(String chainId, Map<String, Object> params) {
    Chain chain = ChainHelper.getByChainCode(chainId);
    return executeAndComparedAndFlow(chain, params);  // 对比/切流/正常 路由
}

// doExecute --- 对应流程图「版本兜底 → 加锁 → 构造上下文 → LiteFlow 执行」
private ChainExecuteResult doExecute(Chain chain, Map<String, Object> params) {
    chain = tryReloadChain(chain);                    // 内存 vs 缓存版本兜底
    chain.getLock().tryLock(2, TimeUnit.SECONDS);
    ChainContext ctx = new ChainContext();
    ChainResultContext resultCtx = new ChainResultContext();
    params = ChainHelper.handleParamsDataType(chain, params);
    flowExecutor.execute2Resp(chain.getCode(), params, ctx, resultCtx);
    return new ChainExecuteResult(resultCtx.getData());
}

// tryReloadChain --- 版本不一致时自动 reload
Chain chainReload = ChainHelper.parseChain(TradeConfig.getProperty(MODULE, chain.getKey()));
if (!chain.getVersion().equals(chainReload.getVersion())) {
    ChainHelper.reload(chainReload);
    return chainReload;
}

端到端主链路

Plaintext 复制代码
页面配置 → 测试验证 → 审批 → compare → flow → publish
  → 配置服务端落库 + Apollo 推送
  → 配置 SDK 拉取/监听 → 本地缓存
  → 规则 SDK 编译加载 → LiteFlow 注册
  → ChainExecutor 执行业务请求

9. 从配置模型到执行模型:LiteFlow 如何承接规则

平台本身并不直接实现一套流程执行引擎,而是把"可视化配置出来的规则"转换成 LiteFlow 能识别的执行模型。这样可以把精力放在规则配置、发布治理、热更新和业务抽象上,底层流程编排、节点调度、上下文传递则交给成熟的流程框架完成。

LiteFlow 里最核心的几个概念是:

LiteFlow 概念 在平台中的含义
Chain 一条规则链路,对应平台里的"链路"
EL 链路编排表达式,描述组件的顺序、条件、分支和组合关系
Node 一个可执行节点,对应平台里的"组件"
FlowBus LiteFlow 的运行时注册表,保存已经加载的 Chain 和 Node
FlowExecutor LiteFlow 的执行入口,根据 chainId 执行对应链路
Context 执行上下文,用来在多个组件之间传递入参、中间结果和最终结果

9.1 从可视化链路到 LiteFlow Chain

管理端配置出来的链路,最终会被发布成一份 Chain JSON。这份 JSON 里包含链路编码、EL 表达式、组件列表、子链路、参数类型、版本号、是否启用、灰度比例等信息。

一个简化后的结构可以理解为:

JSON 复制代码
{
  "key": "address_edit_strategy",
  "el": "THEN(checkCity, checkOrderStatus, buildResult)",
  "enable": true,
  "version": 12,
  "flowPercent": 10,
  "cmpList": [
    {
      "code": "checkCity",
      "componentClass": "SCRIPT",
      "script": "cityId in supportCityList"
    },
    {
      "code": "checkOrderStatus",
      "componentClass": "SCRIPT",
      "script": "orderStatus == 'WAIT_ACCEPT'"
    },
    {
      "code": "buildResult",
      "componentClass": "SCRIPT",
      "script": "setData(chainResultCtx, 'allowEditAddress', true)"
    }
  ]
}

运行时 SDK 启动后,会从配置缓存中读取这份 Chain JSON,然后做两件事:

  • 把链路里的 el 注册成 LiteFlow 的 Chain。

  • 把链路里的每个组件注册成 LiteFlow 的 Node。

也就是说,业务侧看到的是"配置了一条规则链",LiteFlow 底层看到的是"一条可执行 Chain + 多个可执行 Node"。

9.2 EL 表达式负责描述编排关系

EL 是 LiteFlow 的编排语言。平台里的"链路画布"最终会转换成 EL 表达式,交给 LiteFlow 解析。

例如顺序执行可以表达为:

Plaintext 复制代码
THEN(checkCity, checkOrderStatus, buildResult)

带条件分支时,可以表达为:

Plaintext 复制代码
IF(checkRisk, denyResult, THEN(checkCity, buildResult))

这层转换非常关键。可视化平台对用户暴露的是流程图、组件和字段;运行时真正执行的是 LiteFlow 的 EL。这样既能提升配置侧的易用性,又能保持执行侧的稳定性。

9.3 组件会被转换成 LiteFlow Node

平台里的组件有两种典型实现方式:

  • Java 组件:提前写好并注册到 Spring 容器里,适合稳定且复杂的逻辑。

  • 脚本组件:发布时带着脚本内容下发,运行时动态注册成脚本节点,适合频繁变化的判断逻辑。

对于脚本组件,运行时会把组件编码、脚本内容、脚本语言注册到 LiteFlow 的 Node 中。组件执行时,可以从上下文里拿到入参,也可以把结果写回上下文。

比如一个脚本组件可以做这样的事情:

Plaintext 复制代码
读取 cityId、orderStatus
  -> 判断是否命中条件
  -> 把判断结果写入 chainCtx 或 chainResultCtx

在这个过程中,LiteFlow 负责"什么时候执行这个节点",平台负责"这个节点里具体执行什么业务逻辑"。

9.4 FlowBus 是运行时规则注册表

LiteFlow 运行时会把 Chain 和 Node 注册到 FlowBus。可以把 FlowBus 理解为一个内存中的规则执行注册表:

  • chainMap 保存链路定义。

  • nodeMap 保存节点定义。

  • 执行时根据 chainId 找到对应 Chain。

  • Chain 再根据 EL 找到需要执行的 Node。

平台的热更新能力,本质上也是围绕 FlowBus 做的。当配置中心推送新规则后,运行时会解析新的 Chain JSON,再执行:

Plaintext 复制代码
卸载旧 Chain / 旧 Script Node
  -> 注册新 Chain / 新 Script Node
  -> 后续请求自动使用新版本规则

为了避免执行过程中链路被并发修改,运行时通常会在链路维度做必要的锁控制,并通过版本号判断本地缓存是否需要重新加载。

9.5 FlowExecutor 负责真正执行链路

业务服务最终不会直接操作 FlowBus,而是调用平台封装的执行入口。执行入口会先做一些平台层逻辑:

  • 根据 chainId 找到主链。

  • 判断是否存在对比链。

  • 判断是否需要双跑对比。

  • 判断是否命中灰度比例。

  • 构造规则执行上下文。

准备完成后,才会调用 LiteFlow 的 FlowExecutor 执行:

Plaintext 复制代码
FlowExecutor.execute2Resp(chainCode, params, contextBeans)

LiteFlow 会根据 chainCode 找到对应 Chain,解析 EL,按顺序或分支执行 Node。每个 Node 执行时共享同一组上下文,所以前一个组件的计算结果可以被后一个组件继续使用。

9.6 上下文负责串起整个执行过程

规则执行不是简单地返回 true 或 false。许多业务场景需要读取原始入参、保存中间判断结果、输出结构化结果。因此平台在 LiteFlow 的上下文机制上封装了两个核心上下文:

  • ChainContext:保存当前链路、请求入参、中间计算结果,组件可以从这里读取变量或查找其他组件结果。

  • ChainResultContext:保存最终输出结果,结果组件会把业务需要的返回值写到这里。

以前面的地址编辑规则为例:

Plaintext 复制代码
checkCity
  -> 从 ChainContext 读取 cityId
  -> 写入 citySupportEdit = true

checkOrderStatus
  -> 从 ChainContext 读取 orderStatus
  -> 写入 orderCanEditAddress = true

buildResult
  -> 读取前两个组件结果
  -> 向 ChainResultContext 写入 allowEditAddress、reason

最终 ChainExecutor 会把 ChainResultContext 里的数据封装成统一的执行结果返回给业务服务。

9.7 为什么不直接远程调用规则中心执行

这套设计选择"规则中心负责配置和发布,业务服务本地 SDK 负责执行",主要是出于几个考虑:

  • 性能更稳定:规则执行发生在业务服务进程内,不需要每次请求都远程调用规则中心。

  • 可用性更好:配置中心短暂不可用时,业务服务仍然可以使用本地缓存中的规则继续执行。

  • 热更新足够快:配置变更通过监听和定时同步进入本地缓存,再触发 LiteFlow reload。

  • 业务扩展更方便:业务服务可以传入自己的上下文 Bean,也可以注册本地 Java 组件。

所以 LiteFlow 在这里承担的是"本地流程执行内核"的角色,而规则可视化配置平台承担的是"规则资产管理、配置发布和运行时适配"的角色。上面第 6~8 节已从代码层面展开了配置拉取、编译热加载和执行细节,这里补充的是 LiteFlow 层的概念映射。

到这里,平台从"配置如何发布"到"规则如何执行"的主链路已经完整了。不过要理解这个平台为什么能够承载复杂业务规则,还需要回到配置侧的几个基础抽象:空间、类别、选项、组件、链路。它们决定了管理端是否易用,也决定了规则资产能否持续沉淀。

七、几个关键抽象

如果只看模型名,空间、类别、选项、组件、链路这些概念会显得比较抽象。更直观的理解方式,是把它们放到一个具体业务场景里看。

假设有这样一个规则:用户修改订单地址时,系统需要判断是否允许修改。这个判断可能受多种因素影响,比如城市、订单状态、用户类型、车型、是否已经接单、是否命中风险策略等。最终规则可能输出:

JSON 复制代码
{
  "allowEditAddress": true,
  "reason": "当前城市和订单状态允许修改地址"
}

如果写在代码里,这类规则往往会变成连续的 if/else。如果写成普通配置,又容易演变成一段难以维护的 JSON。平台要做的事情,就是把这段业务判断拆成几个层次清晰、可以复用的抽象。

空间:先解决"谁管理谁"的问题

空间可以理解为规则资产的业务边界。比如地址编辑策略、开票策略、解绑策略可以分别放到不同空间里。每个空间有自己的字段、组件、链路和权限,互不影响。

这样可以避免规则平台变成一个缺少边界的公共配置区。地址编辑相关人员只管理地址编辑空间,开票相关人员只管理开票空间。权限边界清晰之后,规则配置才适合开放给更多业务方参与。

类别:让规则资产更容易查找

当一个空间里的字段和组件越来越多时,只靠名称搜索会很难维护。类别就是用来给规则资产分组的。

仍然以上面的地址编辑规则为例,可以把字段分成"订单字段""城市字段""用户字段""风控字段",把组件分成"状态判断""城市判断""结果输出"。后续配置人员想找"订单是否已接单"这样的字段,就不需要在所有字段里搜索,而是可以直接到订单字段分类下查找。

类别本身不参与规则执行,但它决定了平台是否好用。规则资产越多,分类越重要。

选项:把业务字段变成规则可识别的变量

选项是规则中可以使用的字段或变量。它既可以来自业务入参,也可以是组件执行后的中间结果。

比如地址编辑规则里可能会维护这些选项:

选项 含义 类型
cityId 城市 ID 数字
orderStatus 订单状态 枚举
userType 用户类型 枚举
isDriverAccepted 司机是否已接单 布尔
allowEditAddress 是否允许改地址 布尔

有了选项,规则配置就不再是"手写字段名"。配置人员可以从页面选择字段,平台也能基于字段类型做校验,减少把数字、字符串、布尔值混用导致的低级错误。

组件:把一段判断逻辑沉淀为可复用单元

组件可以理解为规则里的一个方法。它接收一些选项作为输入,经过判断后产出一个结果。

例如可以拆出几个组件:

  • 判断城市是否支持地址编辑 :输入 cityId,输出 citySupportEdit

  • 判断订单状态是否允许改地址 :输入 orderStatusisDriverAccepted,输出 orderCanEditAddress

  • 生成最终结果 :输入前面两个判断结果,输出 allowEditAddressreason

组件可以有不同类型:

  • 布尔组件:回答"是不是"的问题,比如"订单是否允许改地址"。

  • 选择组件:回答"选哪一个"的问题,比如"当前场景应该采用哪种处理方式"。

  • 结果组件:负责组织最终输出,比如返回是否允许及原因。

组件也可以有不同实现方式。稳定且复杂的逻辑适合写成 Java 组件,例如需要访问本地服务、复用复杂算法的逻辑;频繁调整、表达式较简单的逻辑适合脚本组件,例如某几个字段之间的条件判断。

链路:把组件编排成完整业务决策

链路就是完整的规则流程。它把多个组件按顺序、条件和分支组织起来,形成一次完整的业务决策。

地址编辑规则的链路可以抽象成:

Plaintext 复制代码
开始
  -> 判断城市是否支持地址编辑
  -> 判断订单状态是否允许改地址
  -> 如果两个判断都通过,输出 allowEditAddress = true
  -> 否则输出 allowEditAddress = false 及原因
结束

在运行时,业务服务只需要调用:

Plaintext 复制代码
execute("address_edit_strategy", requestParams)

SDK 会根据链路定义执行对应组件,把中间结果放入上下文,最终返回结构化结果。

这几个抽象组合起来之后,规则配置就不再是"配置一段 JSON",而是形成了一套可维护的规则资产体系:空间解决隔离和权限,类别解决组织和查找,选项解决字段标准化,组件解决逻辑复用,链路解决完整决策编排。

当这些抽象和底层运行机制都具备之后,平台带来的变化就不只是"配置更方便",而是规则从开发、验证、上线到回滚的整个生命周期都被重新组织了一遍。

八、实践效果

规则可视化配置平台适合承接这类场景:

  • 判断条件多,且经常变化的业务开关。

  • 需要输出结构化结果的业务策略。

  • 需要按城市、渠道、业务线、人群、订单状态等维度分支的规则。

  • 需要发布审批、线上对比、灰度切流和快速回滚的高风险配置。

  • 希望沉淀字段、组件和链路资产的复杂业务域。

效率提升

改造前 改造后
配置结构自由,人工维护容易出错 页面可视化编辑,字段、组件、链路结构化管理
回归场景困难,需要构造真实业务链路 页面测试用例可直接构造参数并执行规则
规则发布后责任人感知弱 审批、通知、发布卡片统一管控
规则散落在代码和配置中,复用困难 字段、组件、链路沉淀为规则资产
变更一次性全量生效风险高 双跑对比、灰度切流、快速回滚

这些效果并不是某个单一功能带来的,而是结构化表达、发布治理、本地执行和热更新机制共同作用的结果。

九、总结

规则可视化配置平台的价值,不只是把规则"搬到页面上配置",而是把复杂业务规则从散落的代码和配置中抽象出来,形成一套完整的表达、验证、发布、运行和治理机制。

它背后的关键设计可以概括为三点:

  • 用字段、组件、链路把复杂业务规则结构化。

  • 用配置中心和运行时 SDK 把规则安全地下发并在业务服务本地执行。

  • 用测试用例、审批、双跑、灰度和快照回滚控制规则变更风险。

当业务规则越来越多、变化越来越频繁时,单纯依赖代码分支或普通配置中心都会逐渐难以维护。把规则平台化、资产化、治理化,才是长期可维护的方向。

相关推荐
爱勇宝1 小时前
AI 都能写代码了,还要学计算机吗?
前端·后端·程序员
cfm_29141 小时前
Spring监听器
java·spring boot·后端
逝水无殇1 小时前
C# 特性详解
开发语言·后端·c#
cfm_29141 小时前
SpringBoot 数据库全栈整合
数据库·spring boot·后端
xn71331 小时前
别再把 MCP Roots 当安全沙箱:一个符号链接就能逃逸目录
人工智能·后端·mcp
SamDeepThinking1 小时前
Java面向对象在JVM里怎么实现
java·后端·面试
学以智用2 小时前
HTTP 请求头 Headers 完整详解
前端·后端
妙码生花2 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(三十五):控制台静态页面、深入研究时间格式化方案
后端·go·ai编程
程序猿大帅2 小时前
Java 虚拟线程发布两年后,我们把它从生产环境移除了
后端