LiteFlow LiteFlow
首页
  • v2.16.1 (当前版本)
  • What's New

    • What' s New In LiteFlow v2.16.1?
    • What' s New In LiteFlow v2.16.0?
  • 历史版本

    • v2.15.X
    • v2.13.X
    • v2.12.X
    • v2.11.X
    • v2.10.X
    • v2.9.X
    • v2.8.X
  • 升级指南

    • 2.13.0升级指南
    • 2.12.4升级指南
    • 2.12.0升级指南
    • 升级到2.9.3说明
    • 升级到2.9.X说明
    • 升级到2.8.X说明
    • 升级到2.7.X说明
AI Agent
AI Skill
IDEA 插件
  • 答疑解惑

    • 常见问题
    • 如何理解上下文这个概念?
    • Slot是一个什么样的概念?
  • 项目与社区

    • 项目介绍
    • 项目成员
    • 更新记录
    • 参与开发
    • 加入群聊
    • 谁在使用
赞助
GitHub (opens new window)

广告采用随机轮播方式显示 ❤️成为赞助商
首页
  • v2.16.1 (当前版本)
  • What's New

    • What' s New In LiteFlow v2.16.1?
    • What' s New In LiteFlow v2.16.0?
  • 历史版本

    • v2.15.X
    • v2.13.X
    • v2.12.X
    • v2.11.X
    • v2.10.X
    • v2.9.X
    • v2.8.X
  • 升级指南

    • 2.13.0升级指南
    • 2.12.4升级指南
    • 2.12.0升级指南
    • 升级到2.9.3说明
    • 升级到2.9.X说明
    • 升级到2.8.X说明
    • 升级到2.7.X说明
AI Agent
AI Skill
IDEA 插件
  • 答疑解惑

    • 常见问题
    • 如何理解上下文这个概念?
    • Slot是一个什么样的概念?
  • 项目与社区

    • 项目介绍
    • 项目成员
    • 更新记录
    • 参与开发
    • 加入群聊
    • 谁在使用
赞助
GitHub (opens new window)
  • 🍤LiteFlow简介
  • 🍓项目特性
  • 🧁环境支持

    • ☕️JDK支持度
    • 🌿Springboot支持度
    • 🌱Spring的支持度
  • 🍟快速开始(Hello world)

    • 🍄说明
    • 🌿Springboot场景安装运行
    • 🌱Spring场景安装运行
    • 🍩Solon场景安装运行
    • 🌵其他场景安装运行
  • 🍢配置项

    • 🍄说明
    • 🌿Springboot下的配置项
    • 🌱Spring下的配置项
    • 🍩Solon下的配置项
    • 🌵其他场景代码设置配置项
  • 🔗组件

    • 🛍继承式组件

      • 📎普通组件
      • ✂️选择组件
      • ⛓布尔组件
      • 🧬次数循环组件
      • ⌛️迭代循环组件
      • 🏄LiteflowComponent
      • 🛀组件内方法覆盖和调用
    • 🎁声明式组件

      • 🥭什么叫声明式组件
      • 🧅类级别式声明
      • 🥥方法级别式声明
  • 🧩EL规则

    • 🍄说明
    • 🌴串行编排
    • 🎋并行编排
    • 🌾选择编排
    • 🌵条件编排
    • 🌳循环编排
    • 🥦异步循环模式
    • 🎃捕获异常表达式
    • 🍄与或非表达式
    • 🍁使用子流程
    • 🍂使用子变量
    • 💐复杂编排例子
    • 🍒前置和后置编排
    • 🍉组件参数语法

      • 说明
      • tag语法
      • data语法
      • bind语法
    • 🫐重试语法
    • ⏱️超时控制语法
    • 🥯链路继承
    • 🔆验证规则
    • 🌰关于注释
    • 🌻关于分号
    • 🐚组件名包装
  • 🌮上下文

    • 🍄说明
    • 🌯数据上下文的定义和使用
    • 🪶用初始化好的上下文传入
    • 🥨给上下文设置别名
    • 🥙上下文参数注入
    • 🪴用表达式获取上下文参数
  • 🛩执行器

    • 🍄说明
    • 🎡执行方法
    • 🎢流程入参
    • 🎈LiteflowResponse对象
    • 🪃直接执行EL规则
  • 🍋脚本组件

    • 🌭脚本语言介绍
    • 🍫脚本语言种类

      • ☕️Java脚本引擎
      • 🥏Groovy脚本引擎
      • 🧀Javascript脚本引擎
      • 🥞QLExpress脚本引擎
      • 🍧Python脚本引擎
      • 🍝Lua脚本引擎
      • 🥐Aviator脚本引擎
      • 🥠Kotlin脚本引擎
    • 🍣脚本与Java进行交互
    • 🍱多脚本语言混合共存
    • 🌯文件脚本的定义
    • 🍘动态刷新脚本
    • 🍦验证脚本
    • 🗑卸载脚本
  • 🗂规则配置源

    • 📕本地规则文件配置
    • 📘SQL数据库配置源(旧)
    • 📗ZK规则文件配置源(旧)
    • 📋Nacos配置源(旧)
    • 🗄Etcd配置源(旧)
    • 📜Apollo配置源(旧)
    • 📑Redis配置源(旧)

      • 配置说明
      • 轮询模式配置
      • 订阅模式配置
    • 📙自定义配置源
  • 🏦Rule-DB模式

    • 🏦Rule-DB是什么
    • 🐬快速开始(SQL)
    • 🐘快速开始(PostgreSQL)
    • 🍃快速开始(MongoDB)
    • 📕快速开始(Redis)
    • 📗快速开始(ZooKeeper)
    • 🗄快速开始(Etcd)
    • 📋快速开始(Nacos)
    • 🍢配置项参考
    • 🗃存储结构参考
      • SQL 四张表
        • 已有SQL部署的升级
      • PostgreSQL四张表
      • MongoDB Collection
      • Redis键结构
      • ZooKeeper路径结构
      • Etcd键结构
      • Nacos Catalog
    • 📤发布API与写入规范
    • ⚖️一致性与收敛模型
    • 🪁内存与性能
    • 🔭可观测性与降级
    • 🚧限制与迁移
  • 🍼元数据管理

    • ⛰元数据操作器
    • 🍖平滑热刷新
    • 🍮启动不检查规则
    • 🥨启动不检查脚本
  • 🌌异步中的线程池

    • 💧说明
    • 🐋FlowExecutor层面的线程池
    • 🐠组件异步层面的线程池
    • 🪶虚拟线程
  • 🎲动态构造

    • 🍄说明
    • 🥜构造Node
    • 🌰构造EL
    • 🍞构造Chain
  • 🧮决策路由

    • 🏖概念以及介绍
    • 🍽决策路由用法
  • 😸生命周期

    • 🐮启动时生命周期
    • 🐳执行时生命周期
  • 🎨高级特性

    • 🍌本地规则文件监听
    • 🥠组件降级
    • 🍑组件别名
    • 🥝组件事件回调
    • 🐋组件回滚
    • 🥑隐式子流程
    • 🫐活跃规则保活策略
    • 🍕私有投递
    • 🍪组件切面
    • 🍡步骤信息
    • 🧊异常
    • 🧇打印信息详解
    • 🧁自定义请求Id
    • 🫕快速解析模式
    • 🌭不同格式规则加载
    • 🍿自定义组件执行器
    • 🍥简单监控
    • 🧉XML的DTD
  • 📈指标监控

    • 📈指标监控概述
    • 🚀快速接入
    • 📋指标目录
    • 🔌Actuator端点说明
    • 📐分位与直方图
    • 🔍常用PromQL与告警
    • ⚡性能影响与基数控制
  • ⛱测试用例以及示例

    • 🪁测试用例
    • 🪀DEMO案例
  • 🪂性能表现
  • v2.16.X文档
  • 🏦Rule-DB模式
铂赛东
2026-07-25
目录

🗃存储结构参考

版本支持:v2.16.1+

# SQL 四张表

DDL随liteflow-rule-db-sql模块一起提供,在模块内的src/main/resources/sql/ddl-mysql.sql。表名前缀可以配,默认是lf_,字段名则是固定的。

lf_chain — 主键 (application_name, chain_id)

列 类型 说明
application_name VARCHAR(64) 应用隔离维度
chain_id VARCHAR(128) chain 标识
namespace VARCHAR(64) NULL 命名空间
el_data TEXT EL 表达式
route_data TEXT NULL 路由 EL(route chain 用)
version BIGINT 每次发布 +1
content_md5 CHAR(32) el_data的MD5,注意不含route_data
enable TINYINT 1 启用 / 0 停用
gmt_create / gmt_modified DATETIME 创建/修改时间

lf_script — 主键 (application_name, node_id)

列 类型 说明
application_name VARCHAR(64) 应用隔离维度
node_id VARCHAR(128) 脚本节点标识
script_name VARCHAR(128) NULL 节点名
script_type VARCHAR(32) 脚本类型,对齐NodeTypeEnum,可选script、boolean_script、switch_script、for_script。WHILE和ITERATOR属于非脚本节点类型,没有*_script变体
script_language VARCHAR(32) NULL groovy、js、python之类,为空则用全局默认
script_data TEXT 脚本源码
content_md5 CHAR(32) script_data 的 MD5
version / enable / gmt_create / gmt_modified 同上

lf_change_log — 主键 seq AUTO_INCREMENT,索引 (application_name, seq)

列 说明
seq 整张表全局单调递增的变更序号。不同应用共享同一个序号空间,所以跨应用看到跳号是正常的。
application_name 应用隔离维度
target_type CHAIN / SCRIPT
target_id chainId / nodeId
op UPSERT / DELETE
version 变更后的版本号
gmt_create 创建时间

lf_change_lock — 单行发布顺序锁

列 说明
lock_id 主键,必须存在,而且只用值1这一行。Publisher在事务内执行SELECT ... FOR UPDATE,持锁到提交或回滚。

这把锁的作用是让change log序号的分配顺序和发布事务的提交顺序对齐,免得并发事务先拿到较小的seq却后提交,导致轮询节点越过尚未提交的变更。它是整套表的全局锁,不按application_name拆分。

change_log的清理

change_log是可以定期清理的,建议保留7天。但要注意SQL和PostgreSQL用的是跨应用共享的全局序号,运行时不会去查MIN(seq)来判断每一个缺口。水位前进了却读不到本应用记录时会请求全量对账;而如果裁剪之后仍然能读到更晚的本应用记录,那么缺失的变化可能要等下一次周期对账才补齐。

所以清理不会破坏最终的正确性,但可能让个别节点失去seq轮询这条快速收敛路径。清理窗口应该明显大于节点的最长离线时间,同时保留周期对账。

# 已有SQL部署的升级

从没有change_lock的旧版本升级时,请务必在恢复发布流量之前执行下面的迁移。示例用的是默认前缀lf_,如果你自定义了table-prefix,记得同步替换表名。

CREATE TABLE IF NOT EXISTS `lf_change_lock` (
  `lock_id` TINYINT NOT NULL,
  PRIMARY KEY (`lock_id`)
) DEFAULT CHARACTER SET utf8mb4;
INSERT IGNORE INTO `lf_change_lock` (`lock_id`) VALUES (1);

表建了但lock_id = 1这行缺失,发布同样会失败。所以不要删除或者修改这行数据。

# PostgreSQL四张表

PostgreSQL用的是chain、script、change_log、change_lock四张表,逻辑字段和SQL后端一致,只是DDL里用了BIGSERIAL、BOOLEAN和TIMESTAMPTZ。完整DDL在模块内的src/main/resources/postgresql/ddl.sql。

已有的PostgreSQL部署同样要在恢复发布流量之前执行下面的迁移:

CREATE TABLE IF NOT EXISTS lf_change_lock (
  lock_id SMALLINT PRIMARY KEY
);
INSERT INTO lf_change_lock (lock_id) VALUES (1)
ON CONFLICT (lock_id) DO NOTHING;

# MongoDB Collection

默认用下面四个Collection:

Collection 内容
lf_chain chain的元数据和正文,_id由applicationName加chainId组成
lf_script script的元数据和正文
lf_sequence 每个applicationName独立的连续seq
lf_change_log seq、目标类型、目标id、操作和业务版本

Manifest查询只投影元数据字段,不读EL和脚本正文,并通过快照事务保证元数据和sequence基线是一致的。Publisher则在一个MongoDB多文档事务里同时更新内容、sequence和change log。

# Redis键结构

键前缀可配,默认是lf,下面的{app}就是application-name。如果配了key-hash-tag,键布局会变成{prefix}:{hashTag}:{app}:...。这些键都没有设置过期时间,因为规则的权威源不该被自动清理,删除请走removeChain和removeScript。

键 类型 内容
{prefix}:{app}:chain:{chainId} HASH 字段:el / route / namespace / version / md5 / enable
{prefix}:{app}:script:{nodeId} HASH 字段:script / name / type / language / version / md5 / enable
{prefix}:{app}:chain-ids SET 所有chain的id集合,只存id,不存版本
{prefix}:{app}:script-ids SET 所有script的id集合
{prefix}:{app}:seq STRING 全局变更序号,用INCR自增
{prefix}:{app}:changelog ZSET score = seq,member = JSON{seq, targetType, targetId, op, version}

那两个*-ids的SET是有意这么设计的:清单和对账先readAll拿到全部id集合,再用一个pipeline(RBatch)批量读取每个chain和script的元数据HASH,一次往返就把id和版本拿齐了,不用逐个去扫内容键。

changelog需要定期裁剪

changelog这个ZSet每次发布都会ZADD一条,框架不会自动收缩,所以长期高频发布会一直占内存。建议定期清理一下,比如只保留最近7天或者最近N条:

ZREMRANGEBYSCORE lf:{app}:changelog 0 {要清理到的seq}

裁剪不影响正确性。节点发现自己的位点低于ZSet里最小的seq,也就是断档了,会自动触发一次全量对账。

# ZooKeeper路径结构

所有规则节点挂在{root}/{app}/...下,root默认是/liteflow。每个chain和script都拆成两个znode,meta存轻量指纹供清单遍历,content存全文供按需拉取。

路径 内容
{root}/{app}/chains/meta/{chainId} chain元数据,version、md5、enable等
{root}/{app}/chains/content/{chainId} chain的EL全文,带版本
{root}/{app}/scripts/meta/{nodeId} script元数据
{root}/{app}/scripts/content/{nodeId} script源码全文

清单只遍历meta子节点,拿到id和版本,content节点要等懒加载时才读。变更序号用的是zxid,也就是节点的mzxid,所以zk后端没有独立的changelog或seq节点,watch直接监听meta节点的增删改,每个事件自带zxid作为序号。

zk的znode数量随规则数线性增长,规则量大的时候注意一下zk的znode和quota限制。zk这边没有类似SQL change_log那种需要清理的日志结构。

# Etcd键结构

布局和zk是同构的,只是把znode换成了etcd的KV,用前缀key而不是层级节点:

key前缀 内容
{root}/{app}/chains/meta/{chainId} chain元数据
{root}/{app}/chains/content/{chainId} chain的EL全文
{root}/{app}/scripts/meta/{nodeId} script元数据
{root}/{app}/scripts/content/{nodeId} script源码全文

变更序号用etcd的KV revision,watch按revision区间订阅meta前缀。etcd会定期compact历史revision,所以一旦节点位点落后到已经被compact的revision,watch会报revision compacted错误,这时会自动降级成一次全量对账,之后再从最新revision续上watch。和zk一样,etcd后端也没有独立的changelog。

# Nacos Catalog

Nacos后端按applicationName存一条JSON Catalog:

dataId = {data-id-prefix}.{application-name}.catalog.json
group  = {group}

Catalog的逻辑结构是下面这样,数组里保存的是完整的chain和script记录:

{
  "schemaVersion": 1,
  "sequence": 2,
  "chains": [
    {
      "chainId": "orderChain",
      "el": "THEN(a,b)",
      "route": null,
      "namespace": null,
      "version": 1,
      "md5": "...",
      "enable": true
    }
  ],
  "scripts": [],
  "lastChange": {
    "seq": 2,
    "targetType": "CHAIN",
    "targetId": "orderChain",
    "op": "UPSERT",
    "version": 1
  }
}

sequence是应用级的连续序号,lastChange.seq必须和它相等。读取端会严格校验schema、字段类型、重复id、正文MD5、业务版本,以及lastChange和最终记录是否一致。Catalog损坏、序号倒退、内容变了但序号没变,这些都会当成存储错误,不会静默接受。

执行端的常驻快照只保存sequence和Catalog的MD5,不会把chains和scripts的正文长期留在Provider里。但Nacos的读取粒度终究是整份配置,所以Manifest对账和每次缓存未命中的正文读取,都要传输、校验并短暂解码整个Catalog。

帮助我们改善此文档 (opens new window)
🍢配置项参考
📤发布API与写入规范

← 🍢配置项参考 📤发布API与写入规范→

Theme by Vdoing | Copyright © 2020-2026 铂赛东 | MIT License
沪ICP备18012955号-2