策略引擎的目标

策略引擎的首要目标是实现快速决策和快速开发. 对于部分业务场景, 产品需要根据实际情况快速新增或调整业务逻辑或需要进行低成本的实验效果观察. 传统的开发模式需要经历需求排期, 需求宣讲, 业务方案设计, 业务方案评审, 实际业务逻辑开发, 功能测试, 灰度发布等一连串的流程, 而对于风控等场景往往需要在一天甚至几小时内完成业务逻辑调整, 传统流程即便去除所有非核心环节, 仅开发和部署就可能需要数小时时间, 无法满足此类需要时效性的场景.

策略引擎将常见的信息固化到入参之中, 并配置各种处置手段, 使得开发的逻辑变为调整策略文本. 而策略变动通常采用热更新的方式, 通常可以在分钟级生效和回滚, 因此可以满足快速决策和快速开发的需求. 得益于快速部署的能力, 产品可以将原本先定需求再开发的模式改为一遍开发一遍观察效果一遍改进的滚动模式, 从而减少了无效需求产生的人力浪费, 并能够根据实际情况更快的进行调整.

按照以上分析, 似乎任何一种支持热更新的脚本语言都可以满足需求, 但实际的策略引擎还需要考虑两个问题:

  1. 语法难度与功能限制性. 语法难度只是在决策过程中, 应该尽量只需要考虑决策逻辑, 而不存在其他需要额外理解的概念或者特殊的语法元素, 也不会应因为语法过于简单而导致需要的功能无法实现.
  2. 功能限制性是指在语法层面不应该轻松产生死循环, 高CPU开销, 扩散型RPC调用等可以轻易将允许策略引擎的机器性能耗尽的操作. 策略文件本质上是由产品和开发共同维护的, 产品并非专业程序员, 因此在语法层面就需要抹除这类BUG产生的可能性.

策略文件的载体

策略引擎需要执行预先配置好的规则, 因此需要有一种文件格式来承载这些规则. 常见的思路是使用JSON文件存储, 其具有语义明确解析简单的优点, 因此通常都算是比较适中的方案. 不过只要意识到这些规则本质上属于是配置, 那么使用YAML格式就显得非常顺理成章了.

YAML格式相较于JSON格式有几个好处:

  1. YAML在设计上就是优先人类可读, 而JSON在设计上是优先机器可读, 因此只要这些规则是需要人写的, 那么人类可读就总是优于机器可读. 例如YAML原生支持注释语法, 而JSON并不支持.
  2. YAML格式的表达能力与JSON相等, 需要使用JSON的场景(例如返回给前端系统渲染展示)时, 可以简单无损的转换为目标格式
  3. YAML格式天然支持字符串等数据类型, 而JSON中如果需要进行类似表达, 则通常需要单引号嵌套双引号之类的操作, 增加语法噪声

例如以本文中涉及的策略语法, 分别以YAML和JSON时, 内容如下:

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
```

```YAML
Meta:
ID: 2602212147
Name: 实例策略
Enabled: true
GrayCount: 10000
Priority: 100
Owner: lizec
Description: |
描述文本可以多行吗?
这里可以写多行描述
Filter:
- Name: 判断事件
When: Req.GetEvent() == 'user_login_check'
ThenList:
- Sys.Log('User Login Check')
Action:
- Name: 加载角色信息
When: true
ThenList:
- Sys.SetVar('VarRole', Req.Attrs['role'])
- Name: 输出
WhenAll:
- VarRole == 'admin'
- Req.UserId == 'alan turing'
ThenList:
- Rsp.SetCode(200)
- Rsp.SetMsg('policy_result', 'welcome turing')

策略文件的语法结构

对于一个单一的策略文件, 其核心逻辑可以概括为一个When->Then的结构, 即满足某个条件时执行特定的动作. 但在实际使用过程中, 往往需要先进行多个条件的判断, 满足全部条件后再执行对应的逻辑. 因此首先将文件分割为FilterAction两个部分, 两者都是数组结构, 但区别在于

  1. 对于Filter中的When条件, 如果满足则执行对应的Then语句. 如果条件不满足, 则退出当前策略的执行.
  2. 对于Action中的When条件, 如果满足则执行对应的Then语句. 如果条件不满足, 则顺序执行下一个元素中的逻辑.

因此正如其名称所表达的一样, Filter主要用于过滤, 而Action主要用于执行业务逻辑.


对于FilterAction中的每一个元素, 其可以分为三个部分, Name是用于表示该元素作用的说明文字, 相当于注释信息. When->Then结构支持多种细分的用法, 具体为

名称 含义
When 接受一个布尔表达式, 返回为真时执行Then语句
WhenAll 接受一组布尔表达式, 返回均为真时执行Then语句
WhenAny 接受一组布尔表达式, 至少1个语句返回真时执行Then语句
ThenList 接受一组表达式, 顺序执行每一行的内容
ThenGroup 接受一组表达式, 并发执行每一行的内容, 并等待所有语句执行完毕

通常情况下, 如果条件较为简单, 则可以单独使用When条件, 从而使得策略看起来更简短. 而对于复杂的逻辑条件, 则可以拆分为多个元素, 依次进行条件判断. 对于耗时不敏感的场景, 通常使用ThenList语句顺序执行, 从而避免产生线程安全问题. 而对于需要控制耗时的场景, 则也可以在谨慎地确保所有语句均处理好了并发问题的前提下使用ThenGroup语句并发执行.


每个策略文件还应该包含一些元信息, 例如策略文件的名称, 是否启用, 灰度比例, 负责人等.

策略的组织结构

策略组织结构 主策略,分组策略

策略引擎的上下文信息

系统函数 执行流控制

策略引擎的入参与出参

入参抽象性, 文档与实际落库数据

数据清洗

赋值与线程安全

可代谢插件系统

事件分裂与转发

执行链路与可观测性

  • 基于落库数据统计输入字段信息

文档化与系统化

使用文档存储还是使用系统存储信息? 使用系统存储就会存在不一致性, 就如同注释和函数不一致. 因此正如最好的代码无需注释, 最好的系统也无需信息

代码支持系统

(语法检查与补全插件, 策略存储仓库)

  • 性能优化

  • 高性能内存名单系统

策略文件分发系统

原子弹最大的秘密是原子弹可以被制造

策略文件分发系统最大的秘密是使用传统Mysql+Redis足以支撑几千台规模机器的拉取数据需求. 采用传统的MySQL数据库+定时轮询即可满足中小规模的需求, 只需要确保定时轮询操作设置了随机的偏移值, 避免所有机器在同一时刻拉取数据即可. 使用这一模式在机器数量达到3000+台之前都可以抗住, 仅需要根据机器数量适当的调整云数据库规则即可.

小规模业务对于决策部分来说, 30台机器可能就已经很多了

只有机器数量大于3000+台, 逐渐逼近10000台时, 才需要考虑进一步优化架构. 此时也仅需要引入一个简单的代理层即可, 代理层定时拉取数据库并缓存到本机内存中, 其他机器查询代理层. 这一模式几乎没有上限, 可以承担超过3000万用户同时使用.


内存名单库的最大问题是确定使用场景, 没有银弹可以解决所有问题. 根据使用场景进行适当的优化

微服务设计

微服务设计从逻辑拆分到一体化服务

AI辅助开发

  • AI与架构设计
  • AI开发与CC: 全自动不可取, AI辅助驾驶更好

最后更新: 2026年08月09日 20:11

版权声明:本文为原创文章,转载请注明出处

原始链接: https://lizec.top/2026/07/20/%E5%A6%82%E4%BD%95%E8%AE%BE%E8%AE%A1%E5%86%B3%E7%AD%96%E5%BC%95%E6%93%8E%E7%B3%BB%E7%BB%9F/