ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Flowable Event Registry 部署实战:Event/Channel 定义、程序化部署与版本管理全解析

Flowable Event Registry 部署实战:Event/Channel 定义、程序化部署与版本管理全解析 Flowable Event Registry 部署实战Event/Channel 定义、程序化部署与版本管理全解析【免费下载链接】flowable-engineA compact and highly efficient workflow and Business Process Management (BPM) platform for developers, system admins and business users.项目地址: https://gitcode.com/GitHub_Trending/fl/flowable-engine本文聚焦 Flowable 开源工作流引擎中Event Registry事件注册中心的部署机制。作为流程引擎的配套事件能力Event Registry 负责从 JMS、Kafka、RabbitMQ 等外部消息源收发事件并驱动 BPMN/CMMN 流程实例。读完本文你将掌握.event与.channel两类定义文件的部署方式类路径、输入流、BAR 打包、定义版本号与 ID 的生成规则以及如何在 Tomcat 中正确放置自定义表达式函数类。一、Event Registry 部署模型概览Event Registry 引擎支持两种类型的定义Definitions二者都以 JSON 格式描述、以文件后缀区分定义类型文件后缀作用落库表Event 定义.event配置事件负载payload结构、关联correlation参数与租户检测FLW_EVENT_DEFINITIONChannel 定义.channel配置入站/出站事件的源或目标JMS、Kafka、RabbitMQ 等适配器FLW_CHANNEL_DEFINITION关于两类定义文件内部的 JSON 结构key、name、correlationParameters、payload、channelType、deserializerType等字段可参阅 Event Registry 入门文档本文重点讲解如何把它们真正部署到引擎中。当 Event Registry 引擎与 Process流程引擎共同使用时.event和.channel资源可以被打包进业务归档Business ArchiveBAR与流程相关的其他资源一同发布。Process 引擎的部署服务会自动把其中的 Event 与 Channel 资源转交给 Event Registry 引擎完成部署开发者无需分别调用两套部署 API。注意业务归档中用于自定义表达式函数的 Java 类不会被自动加入 classpath。所有在 event/channel 定义表达式中用到的自定义类都必须预先存在于 FlowableEvent Registry引擎的 classpath 上。这一点在 部署文档原文 中已明确强调下文Java 类与 classpath一节会给出具体放置方案。二、Event 定义部署2.1 什么是 Event 定义Event 定义配置了事件负载结构以及关联correlation与租户tenant检测。它描述了事件所携带的字段、用于将事件匹配到具体流程/案例实例订阅的关联参数以及多租户环境下的事件归属。部署时一条新的定义记录会被插入FLW_EVENT_DEFINITION表。从源码看部署的核心编排逻辑位于 EventDefinitionDeployer.java 的deploy(EventDeploymentEntity)方法它依次完成解析部署资源为ParsedDeployment校验同一次部署内不出现重复 keyverifyEventDefinitionsDoNotShareKeys把部署的租户 ID、部署 ID 复制到定义实体上定义继承租户为定义设置资源名计算版本号并生成 ID、持久化定义更新缓存与运行时产物。2.2 程序化部署 Event 定义通过repositoryService.createDeployment()即可完成部署。最基本的方式是从 classpath 加载.event资源String eventDefinition path/to/definition-one.event; // 别忘了 .event 扩展名 repositoryService.createDeployment() .name(Deployment of Event definition-one) .addClasspathResource(eventDefinition) .deploy();也可以使用addInputStream等方法把 Event 定义加入部署。以下示例从外部文件部署 Event 定义File eventFile new File(/path/to/definition-two.event); // 别忘了 .event 扩展名 repositoryService.createDeployment() .name(Deployment of Event definition-two) .addInputStream(eventFile.getName(), new FileInputStream(eventFile)) .deploy();部署的name可以是任意文本但资源名必须始终包含合法的 Event 定义后缀.event引擎正是通过后缀来识别并路由到 Event 定义的解析器。除了文档给出的两种方式从源码 EventDeploymentBuilderImpl.java 还可以看到更多可用的构建方法它们统一通过EventDeploymentBuilder接口暴露方法作用addClasspathResource(String)从 classpath 读取资源加入部署addInputStream(String, InputStream)从输入流加入资源内部会转为字节数组addString(String, String)直接以字符串形式加入资源addEventDefinition(String, String)/addEventDefinitionBytes(String, byte[])以事件定义语义加入文本/字节内容addChannelDefinition(String, String)/addChannelDefinitionBytes(String, byte[])以通道定义语义加入文本/字节内容name(String)/category(String)/tenantId(String)/parentDeploymentId(String)设置部署名称、分类、租户与父部署 IDenableDuplicateFiltering()开启重复部署过滤deploy()提交部署内部调用repositoryService.deploy(this)其中tenantId与parentDeploymentId对多租户与部署与流程打包发布BAR场景尤其关键copyDeploymentValuesToEventDefinitions会把部署上的租户 ID 复制给其中的每个定义。仓库中的测试资源 simpleEvent.event 是一个可直接运行的部署样例{ key: myEvent, name: My event, correlationParameters: [ { name: customerId, type: string } ], payload: [ { name: payload1, type: string }, { name: payload2, type: integer } ] }三、Channel 定义部署3.1 什么是 Channel 定义Channel 定义配置了入站inbound或出站outbound事件的源或目标。默认情况下 Flowable Event Registry 支持JMS、Kafka、RabbitMQ三种源/目标适配器同时允许通过扩展机制注册其他适配器类型。部署时一条新的定义记录会被插入FLW_CHANNEL_DEFINITION表。从 MySQL 建表脚本 flowable.mysql.create.eventregistry.sql 可以看到FLW_CHANNEL_DEFINITION的结构CREATE TABLE FLW_CHANNEL_DEFINITION (ID_ VARCHAR(255) NOT NULL, NAME_ VARCHAR(255) NULL, VERSION_ INT NULL, KEY_ VARCHAR(255) NULL, CATEGORY_ VARCHAR(255) NULL, TYPE_ VARCHAR(255) NULL, IMPLEMENTATION_ VARCHAR(255) NULL, DEPLOYMENT_ID_ VARCHAR(255) NULL, CREATE_TIME_ datetime(3) NULL, TENANT_ID_ VARCHAR(255) NULL, RESOURCE_NAME_ VARCHAR(255) NULL, DESCRIPTION_ VARCHAR(255) NULL, CONSTRAINT PK_FLW_CHANNEL_DEFINITION PRIMARY KEY (ID_)); CREATE UNIQUE INDEX ACT_IDX_CHANNEL_DEF_UNIQ ON FLW_CHANNEL_DEFINITION(KEY_, VERSION_, TENANT_ID_);注意TYPE_适配器类型如 jms/kafka/rabbitmq与IMPLEMENTATION_实现类字段它们记录了通道背后的适配器实现。3.2 程序化部署 Channel 定义部署 Channel 定义的方式与 Event 定义完全一致只是资源后缀必须是.channelString channelDefinition path/to/definition-one.channel; // 别忘了 .channel 扩展名 repositoryService.createDeployment() .name(Deployment of Channel definition-one) .addClasspathResource(channelDefinition) .deploy();同样支持addInputStream从外部文件部署File channelFile new File(/path/to/definition-two.channel); // 别忘了 .channel 扩展名 repositoryService.createDeployment() .name(Deployment of Channel definition-two) .addInputStream(channelFile.getName(), new FileInputStream(channelFile)) .deploy();部署名称任意但资源名必须始终包含合法的 Channel 定义后缀.channel。仓库测试资源 simpleChannel.channel 展示了入站通道的 JSON 结构可用作参考{ key: testChannel, category: channel, name: Test channel, channelType: inbound, type: jms, destination: test-customer, deserializerType: json, channelEventKeyDetection: { jsonField: eventKeyValue } }对应的单元测试 DeploymentTest.java 同时演示了声明式注解EventDeploymentAnnotation(resources ...)、ChannelDeploymentAnnotation(resources ...)与命令式addClasspathResource(...).deploy()两种用法是理解部署 API 的最佳入门示例。四、Java 类与 classpath 的放置策略所有在 Event 与 Channel 定义中作为自定义表达式函数使用的类在定义被执行时都必须位于引擎 classpath 上。需要特别区分两个时机部署阶段解析 Event/Channel 定义时这些类不必出现在 classpath 上部署只做资源解析与入库执行阶段当事件被处理、表达式被求值时类必须可用否则会抛出类加载异常。如果你在使用演示环境demo setup如flowable-app-rest并希望加入自己的自定义类官方建议将包含自定义类的JAR 放入flowable-app-restwebapp 的 lib 目录即WEB-INF/lib不要遗漏这些自定义类的依赖如有一并放入或者也可以把依赖放入 Tomcat 安装目录的库目录${tomcat.home}/lib。4.1 创建单一应用Single App与其费力地保证所有 Event Registry 引擎都把委托类放在 classpath 上、并维护正确的 Spring 配置你还可以考虑把flowable-restwebapp 内嵌进你自己的 webapp这样整个部署环境中只存在一个EventRegistryEngine。这种单引擎架构避免了多引擎之间的类加载与配置漂移问题也简化了自定义类的分发只需把它放进你自己的 webapp 即可。五、Event 与 Channel 定义的版本管理5.1 版本化规则部署时Flowable 会在写入数据库之前为每个 Event/Channel 定义分配版本号。针对每个定义引擎按以下步骤初始化key、name、version、id四个属性定义 JSON 文件中的key属性→ 用作定义的keyJSON 文件中的name属性→ 用作定义的name第一次部署某个key的定义时版本号为1此后每次以相同key部署新定义版本号会在当前已部署的最大版本上1。key是区分 Event/Channel 定义的依据id是唯一编号用于保证集群环境下Event/Channel 定义标识符在缓存中的唯一性。这些规则在 EventDefinitionDeployer.java 中有直接对应的实现int version 1; EventDefinitionEntity latest mapOfNewEventDefinitionToPreviousVersion.get(eventDefinition); if (latest ! null) { version latest.getVersion() 1; } eventDefinition.setVersion(version); ... eventDefinition.setId(idGenerator.getNextId());而查找已部署的最新版本由 EventDefinitionDeploymentHelper.java 完成先按 key 与租户查找findLatestEventDefinitionByKeyAndTenantId多租户或findLatestEventDefinitionByKey无租户。数据库中由唯一索引ACT_IDX_EVENT_DEF_UNIQ (KEY_, VERSION_, TENANT_ID_)保证同一 key 同一版本在同一租户下不重复参见 建表脚本。5.2 版本演进示例假设我们部署如下 Event 定义key为myEvent{ key: myEvent, name: My event, ... }部署后FLW_EVENT_DEFINITION表将新增一行idkeynameversione29d4126-ed4d-11e6-9e00-7282cbd6ce64myEventMy event1现在再次部署内容更新但 key 保持不变的同一 Event 定义。由于 key 相同版本号在最大版本1基础上 1idkeynameversione29d4126-ed4d-11e6-9e00-7282cbd6ce64myEventMy event1e9c2a6c0-c085-11e6-9096-6ab56fad108amyEventMy event2此时调用eventRegistry.getEventModelByKey(myEvent)会命中版本 2——即该 key 的最新版本。这与查询命令 GetEventModelCmd.java 的实现一致按 key 查询时走findDeployedLatestEventDefinitionByKey获取最新版本定义多租户场景则按租户查最新版并支持fallbackToDefaultTenant回退默认租户。如果继续部署第二个 key 不同的定义{ key: myNewEvent, name: My new event, ... }表内将新增第三行idkeynameversione29d4126-ed4d-11e6-9e00-7282cbd6ce64myEventMy event1e9c2a6c0-c085-11e6-9096-6ab56fad108amyEventMy event2d317d3f7-e948-11e6-9ce6-b28c070b517dmyNewEventMy new event1注意即使两个定义的name相同Flowable Event Registry只依据key属性区分Event 与 Channel 定义因此新 key 的定义从版本 1 开始。六、部署相关的数据库表结构除定义表外一次部署还会产生部署与资源记录。FLW_EVENT_DEPLOYMENT与FLW_EVENT_RESOURCE表见 建表脚本分别保存部署元数据与原始资源字节CREATE TABLE FLW_EVENT_DEPLOYMENT (ID_ VARCHAR(255) NOT NULL, NAME_ VARCHAR(255) NULL, CATEGORY_ VARCHAR(255) NULL, DEPLOY_TIME_ datetime(3) NULL, TENANT_ID_ VARCHAR(255) NULL, PARENT_DEPLOYMENT_ID_ VARCHAR(255) NULL, CONSTRAINT PK_FLW_EVENT_DEPLOYMENT PRIMARY KEY (ID_)); CREATE TABLE FLW_EVENT_RESOURCE (ID_ VARCHAR(255) NOT NULL, NAME_ VARCHAR(255) NULL, DEPLOYMENT_ID_ VARCHAR(255) NULL, RESOURCE_BYTES_ LONGBLOB NULL, CONSTRAINT PK_FLW_EVENT_RESOURCE PRIMARY KEY (ID_)); ALTER TABLE FLW_EVENT_RESOURCE ADD CONSTRAINT FLW_FK_EVENT_RSRC_DPL FOREIGN KEY (DEPLOYMENT_ID_) REFERENCES FLW_EVENT_DEPLOYMENT (ID_);FLW_EVENT_RESOURCE.DEPLOYMENT_ID_通过外键FLW_FK_EVENT_RSRC_DPL关联FLW_EVENT_DEPLOYMENT.ID_原始定义文件内容保存在RESOURCE_BYTES_中。当需要删除部署或重新解析定义时引擎会通过这些资源记录恢复原始 JSON。这解释了为何部署后GetEventDefinitionResourceCmd/GetChannelDefinitionResourceCmd见 cmd 目录仍能按部署 ID 取出定义资源内容。七、小结本文围绕 Flowable Event Registry 的部署链路完整覆盖了.event/.channel两种定义的定位、四种以上的程序化部署方式classpath、输入流、字符串、字节数组、自定义表达式类的 classpath 放置方案以及基于 key 的版本递增与唯一 ID 生成机制。要点回顾后缀决定类型.event进FLW_EVENT_DEFINITION.channel进FLW_CHANNEL_DEFINITION资源名后缀错误将无法被识别部署入口统一repositoryService.createDeployment()的 builder 链式 API且支持与流程定义一起打包进 BAR 由 Process 引擎转交部署版本只认 key相同 key 递增版本号不同 key 从 1 开始按 key 查询永远返回最新版本类加载分时机部署期不要求自定义类在 classpath执行期必须可用demo 环境请放入 webapp lib 或${tomcat.home}/lib。如需进一步了解 Event/Channel 定义 JSON 的字段语义与 REST 部署端点可继续阅读 Event Registry 配置、Event Registry API 与 Event Registry REST 接口相关实现代码位于 flowable-event-registry 模块。【免费下载链接】flowable-engineA compact and highly efficient workflow and Business Process Management (BPM) platform for developers, system admins and business users.项目地址: https://gitcode.com/GitHub_Trending/fl/flowable-engine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进