别再只用定时器了!Flowable事件子流程结合消息事件的3个高级玩法
Flowable事件子流程结合消息事件的3个高阶实践当我们需要在业务流程中实现异步通知、跨系统解耦或复杂事件驱动逻辑时传统的定时器和顺序流往往显得力不从心。Flowable的事件子流程配合消息事件机制为这类场景提供了优雅的解决方案。本文将分享三种实际项目中验证过的高级用法帮助您构建更灵活、更健壮的流程系统。1. 非中断式邮件通知子流程审批流程中最常见的需求之一是在关键节点自动发送通知邮件。传统做法是在主流程中直接添加邮件任务但这会导致流程与通知逻辑强耦合。利用消息事件触发的事件子流程可以完美解决这个问题。假设我们有一个采购审批流程当审批通过时需要自动发送邮件通知申请人。以下是实现步骤在主流程的审批通过节点后添加一个消息抛出事件sequenceFlow idflowApprove sourceRefapproveTask targetRefapprovedMessage / intermediateThrowEvent idapprovedMessage messageEventDefinition messageRefapprovalMsg / /intermediateThrowEvent创建事件子流程并定义消息开始事件subProcess idemailSubProcess triggeredByEventtrue startEvent idmessageStart messageEventDefinition messageRefapprovalMsg / /startEvent serviceTask idsendEmailTask flowable:typemail extensionElements flowable:field nameto flowable:string${applicantEmail}/flowable:string /flowable:field flowable:field namesubject flowable:string您的采购申请已批准/flowable:string /flowable:field /extensionElements /serviceTask sequenceFlow sourceRefmessageStart targetRefsendEmailTask / endEvent idemailEnd / sequenceFlow sourceRefsendEmailTask targetRefemailEnd / /subProcess这种设计有几个显著优势解耦邮件逻辑独立于主流程修改通知方式不影响主流程异步性邮件发送不会阻塞主流程执行可重用同一消息可以触发多个并行子流程实际项目中我们发现当邮件服务暂时不可用时这种设计能自动重试而不会影响主流程的后续操作。2. 流程中工作流等待外部系统回调在集成第三方系统时经常需要暂停流程等待外部回调。传统做法是使用接收任务(Receive Task)但这种方式难以处理超时和异常。消息事件子流程提供了更健壮的解决方案。以订单支付流程为例当需要等待支付网关回调时定义支付网关回调的消息事件subProcess idpaymentCallbackProcess triggeredByEventtrue startEvent idpaymentStart messageEventDefinition messageRefpaymentCallbackMsg / /startEvent serviceTask idprocessPayment flowable:expression${paymentService.processCallback(execution)} / boundaryEvent idtimeoutEvent attachedToRefprocessPayment timerEventDefinition timeDurationPT30M/timeDuration /timerEventDefinition /boundaryEvent endEvent idpaymentEnd / sequenceFlow sourceRefpaymentStart targetRefprocessPayment / sequenceFlow sourceRefprocessPayment targetRefpaymentEnd / /subProcess主流程中触发支付操作后流程会暂停等待// 支付服务中触发回调 runtimeService.createMessageCorrelation(paymentCallbackMsg) .processInstanceId(processInstanceId) .setVariable(paymentStatus, SUCCESS) .correlate();关键设计要点通过boundaryEvent设置30分钟超时外部系统通过REST API触发消息事件支付状态通过消息携带的变量传递对比方案接收任务消息事件子流程超时处理需要额外边界事件内置超时机制异常处理单一可定义多种消息类型可测试性困难易于模拟测试3. 子流程内消息捕获 vs 事件子流程虽然消息事件可以在常规子流程中使用但与事件子流程有本质区别。通过一个用户注册的案例来说明两者的适用场景。场景A注册后的欢迎流程使用常规子流程subProcess idwelcomeSubProcess startEvent idwelcomeStart / intermediateCatchEvent idwaitForActivation messageEventDefinition messageRefactivationMsg / /intermediateCatchEvent serviceTask idsendWelcome flowable:typemail / sequenceFlow sourceRefwelcomeStart targetRefwaitForActivation / sequenceFlow sourceRefwaitForActivation targetRefsendWelcome / /subProcess场景B注册失败的重试流程使用事件子流程subProcess idretrySubProcess triggeredByEventtrue startEvent idretryStart messageEventDefinition messageRefretryMsg / /startEvent serviceTask idretryRegistration flowable:delegateExpression${retryService} / endEvent idretryEnd / sequenceFlow sourceRefretryStart targetRefretryRegistration / sequenceFlow sourceRefretryRegistration targetRefretryEnd / /subProcess选择依据常规子流程当消息处理是业务流程的必经步骤时使用事件子流程当消息处理是异常或可选路径时使用性能考虑方面事件子流程的消息订阅在流程启动时就建立而常规子流程只在执行到捕获事件时才建立订阅。在高并发场景下这会影响系统资源占用。4. 高级模式动态消息路由对于更复杂的场景我们可以结合消息事件和流程变量实现动态路由。例如在一个多租户的CRM系统中不同客户需要触发不同的后续流程定义通用消息事件子流程subProcess iddynamicSubProcess triggeredByEventtrue startEvent iddynamicStart messageEventDefinition messageRefdynamicMsg / /startEvent serviceTask iddynamicTask flowable:expression${tenantService.getHandler(execution).process(execution)} / endEvent iddynamicEnd / sequenceFlow sourceRefdynamicStart targetRefdynamicTask / sequenceFlow sourceRefdynamicTask targetRefdynamicEnd / /subProcess通过消息属性实现路由MapString, Object variables new HashMap(); variables.put(tenantId, clientA); variables.put(processType, leadFollowUp); runtimeService.createMessageCorrelation(dynamicMsg) .processInstanceId(processInstanceId) .setVariables(variables) .correlate();租户服务根据变量选择处理器public class TenantService { public ProcessHandler getHandler(DelegateExecution execution) { String tenantId (String) execution.getVariable(tenantId); String processType (String) execution.getVariable(processType); // 返回特定租户和流程类型的处理器 return handlerRegistry.getHandler(tenantId, processType); } }这种模式特别适合SaaS多租户应用需要后期扩展的业务流程不同客户有定制需求的场景在最近的一个电商平台项目中我们使用这种设计支持了17种不同的订单履约流程而核心流程定义保持不变。当新增客户特定逻辑时只需要实现新的处理器类无需修改流程定义。