1.定义
信号事件就像一个公共广播系统。当一个信号被“抛出”(Throw)时,它不是发给某一个特定的流程实例,而是向整个 Flowable 引擎广播一个通知。所有正在等待这个信号的流程实例(们)都会同时“捕获”(Catch)到它,并继续执行。这是一种 一对多 (One-to-many) 的通信模式。
2. 关键特性
全局性 (Global):信号的作用域是整个流程引擎,而不是单个流程实例。
基于名称 (Name-based):信号的识别完全依赖于它的名称。例如,一个名为 user-cancelled 的信号会被所有监听 user-cancelled 的事件捕获。
解耦 (Decoupled):发送信号的实体(可以是另一个流程,也可以是外部的 Java 代码)与接收信号的流程实例之间是完全解耦的。发送者不需要知道谁在监听,监听者也不需要知道是谁发送的。
同步或异步:信号的发送可以是同步的,也可以是异步的,这取决于你的配置和使用场景。
3. 信号事件的类型和用法
信号事件可以在流程的多个地方使用:
信号启动事件 (Signal Start Event)
图标:一个圆圈,中间有一个三角形。
作用:用一个全局信号来启动一个新的流程实例。
场景:当一个外部系统完成了一项重要的初始化任务(如“日终结算完成”),它就可以发送一个信号,所有以此信号为起点的流程(如“生成日报表”、“数据归档”等)都会被启动。
中间信号捕获事件 (Intermediate Signal Catching Event)
图标:双层圆圈,中间有一个空心三角形。
作用:流程执行到这个节点时会暂停,进入等待状态,直到它所定义的信号被广播出来。
场景:一个订单流程在“等待付款”后,进入了“备货”环节。此时如果收到一个全局的 warehouse-maintenance (仓库维护) 信号,流程可以捕获它并进入一个“暂停备货”的路径。
中间信号抛出事件 (Intermediate Signal Throwing Event)
图标:双层圆圈,中间有一个实心三角形。
作用:流程执行到这个节点时,会主动向外广播一个信号。
场景:一个“新员工入职”流程成功完成后,它会抛出一个 new-employee-onboarded 信号,通知所有相关的其他流程(如“权限分配流程”、“资产申领流程”)可以开始了。
信号边界事件 (Signal Boundary Event)
图标:附加在活动(如用户任务)边框上的一个圆圈,中间有一个三角形。
作用:当它所附加的活动正在执行时,如果捕获到指定的信号,它会中断(或不中断,可配置)该活动,并将流程引向另一条路径。
场景:在一个“经理审批”的用户任务上,附加一个 order-cancelled-by-user 信号边界事件。如果经理还没审批,但用户通过另一个系统取消了订单,该系统发送一个取消信号,审批任务就会被立即中断,流程直接走向“关闭订单”的路径。
4. 在 Flowable 中如何触发信号
你主要通过 RuntimeService 来发送一个全局信号:
// 注入 RuntimeService
@Autowired
private RuntimeService runtimeService;
public void sendGlobalSignal() {
// 发送一个名为 "my-signal" 的信号
// 所有等待这个信号的流程实例都会被激活
runtimeService.signalEventReceived("my-signal");
// 你也可以在发送信号的同时传递一些流程变量
Map<String, Object> variables = new HashMap<>();
variables.put("reason", "Urgent escalation");
runtimeService.signalEventReceived("my-signal", variables);
}
信号事件 vs. 消息事件 (Signal vs. Message)
这是理解这两种事件的关键部分。如果你把信号理解为“广播”,那么请把消息理解为**“点对点通信”,就像发送一封指定收件人的电子邮件**。
特性 | 信号事件 (Signal Event) | 消息事件 (Message Event) |
---|---|---|
核心思想 | 广播 (Broadcast) | 单点投递 (Point-to-Point) |
目标 | 一对多 (One-to-Many)。 发送给所有正在监听此信号的流程实例 | 一对一 (One-to-One)。 明确发送给某一个特定的流程实例 |
标识方式 | 仅通过信号名称 (signalName)。 | 通过消息名称 (messageName) + 实例关联信息。 引擎需要知道把消息送给哪个实例,通常通过流程实例ID或业务标识(Business Key)来关联。 |
典型用途 | 1. 系统级状态同步(如系统维护)。 2. 触发多个相关流程(如入职成功后触发多个后续流程)。 3. 跨流程的全局取消或通知。 | 1. 两个流程实例之间的直接通信。 2. 外部系统回调(如支付成功后,支付网关回调通知那笔订单的流程)。 3. 启动一个与特定业务数据关联的新流程。 |
Flowable API (触发方式) | runtimeService.signalEventReceived(signalName, …) | runtimeService.messageEventReceived(messageName, executionId, …) 注意:这里需要一个 executionId 来定位到具体的执行实例。 |
类比 | 公共广播 / AM/FM 电台 电台只管广播,所有调到这个频道的收音机都能收到。 | 电子邮件 / 快递 必须有明确的收件人地址,才能准确投递。 |
何时使用信号事件?
当你需要通知多个流程实例某个事件发生了。
当发送方不关心也不需要知道谁是接收方时。
用于实现全局的、跨流程的协调。
例子:一个管理员按下“系统紧急暂停”按钮,所有正在处理的订单流程都应该暂停。
何时使用消息事件?
当你需要与某个特定的流程实例进行交互时。
当一个流程需要等待来自外部系统的、针对它自己的回调时。
当一个流程需要向另一个已知的流程发送指令时。
例子:一个订单流程调用了支付接口,然后等待支付网关返回“支付成功”的消息。这个消息只应该继续这一个订单的流程,而不是其他订单的。
业务场景
我们将设计一个非常贴合实际业务的场景:电商平台的订单履行流程,与IT部门的系统维护流程之间的交互。
这个场景完美地诠释了信号事件的“广播”和“解耦”特性。
主流程:订单履行流程 (Order Fulfillment Process)
一个客户下单后,系统启动此流程。
流程中有一个关键步骤是“与WMS(仓库管理系统)同步,分配库存”。这是一个自动化的服务任务。
正常情况下,分配库存后,流程继续走向“打包发货”。
触发流程:IT系统维护流程 (IT System Maintenance Process)
当IT部门需要对WMS进行维护时,他们会启动一个维护流程。
这个流程首先会向整个系统广播一个“维护开始”的信号。
在维护工作完成后,它会再次广播一个“维护结束”的信号。
核心交互逻辑
当“订单履行流程”正好执行到“分配库存”这一步时,如果IT部门发出了“维护开始”的信号,订单流程应该能捕获到这个信号。
捕获到信号后,订单流程应该中断当前的“分配库存”任务,并进入一个“等待维护结束”的状态。
当IT部门完成维护并发出“维护结束”的信号后,订单流程应该能再次捕获到这个新信号,然后重新尝试“分配库存”任务。
这里,信号事件是最佳选择,因为IT部门不需要知道当前有多少个订单正在运行,它只需要发出一个全局通知,所有相关的流程都应该能响应。
文件一:订单履行流程 (orderFulfillmentProcess.bpmn20.xml)
作用:这是主业务流程。它处理单个订单,并且能够响应外部的系统维护信号。
核心逻辑:
在“与WMS同步分配库存”任务上,附加了一个边界信号事件,用于捕获“维护开始”信号 (wmsMaintenanceStart)。
如果捕获到信号,流程会中断同步任务,进入等待状态。
在等待状态,一个中间信号捕获事件会等待“维护结束”信号 (wmsMaintenanceEnd)。
收到结束信号后,流程会重新尝试同步库存。
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
xmlns:flowable="http://flowable.org/bpmn"
targetNamespace="http://www.flowable.org/processdef">
<!--
定义此流程关心(监听)的信号。
即使信号由其他流程抛出,在这里定义可以使模型自描述。
-->
<signal id="wmsMaintenanceStartSignal" name="wmsMaintenanceStart"></signal>
<signal id="wmsMaintenanceEndSignal" name="wmsMaintenanceEnd"></signal>
<process id="orderFulfillmentProcess" name="E-commerce Order Fulfillment Process" isExecutable="true">
<startEvent id="startOrder" name="订单已创建"></startEvent>
<sequenceFlow id="flow_to_review" sourceRef="startOrder" targetRef="reviewOrder"></sequenceFlow>
<userTask id="reviewOrder" name="审核订单"></userTask>
<sequenceFlow id="flow_to_sync" sourceRef="reviewOrder" targetRef="syncWmsTask"></sequenceFlow>
<serviceTask id="syncWmsTask" name="与WMS同步分配库存" flowable:class="com.example.flowable.delegate.SyncWmsDelegate"></serviceTask>
<sequenceFlow id="flow_to_ship" sourceRef="syncWmsTask" targetRef="shipOrder"></sequenceFlow>
<!-- 边界信号事件:捕获“维护开始”信号 -->
<boundaryEvent id="catchMaintenanceStart" attachedToRef="syncWmsTask" cancelActivity="true">
<signalEventDefinition signalRef="wmsMaintenanceStartSignal"></signalEventDefinition>
</boundaryEvent>
<sequenceFlow id="flow_to_wait" sourceRef="catchMaintenanceStart" targetRef="waitForMaintenanceEnd"></sequenceFlow>
<!-- 中间信号捕获事件:等待“维护结束”信号 -->
<intermediateCatchEvent id="waitForMaintenanceEnd" name="等待WMS维护结束">
<signalEventDefinition signalRef="wmsMaintenanceEndSignal"></signalEventDefinition>
</intermediateCatchEvent>
<sequenceFlow id="flow_to_retry" sourceRef="waitForMaintenanceEnd" targetRef="syncWmsTask"></sequenceFlow>
<userTask id="shipOrder" name="打包并安排发货"></userTask>
<sequenceFlow id="flow_to_end" sourceRef="shipOrder" targetRef="endOrder"></sequenceFlow>
<endEvent id="endOrder" name="订单完成"></endEvent>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_orderFulfillmentProcess">
<bpmndi:BPMNPlane bpmnElement="orderFulfillmentProcess" id="BPMNPlane_orderFulfillmentProcess">
<bpmndi:BPMNShape bpmnElement="startOrder" id="BPMNShape_startOrder">
<omgdc:Bounds x="100" y="200" width="36" height="36"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="reviewOrder" id="BPMNShape_reviewOrder">
<omgdc:Bounds x="180" y="183" width="105" height="70"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="syncWmsTask" id="BPMNShape_syncWmsTask">
<omgdc:Bounds x="330" y="183" width="105" height="70"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="shipOrder" id="BPMNShape_shipOrder">
<omgdc:Bounds x="480" y="183" width="105" height="70"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endOrder" id="BPMNShape_endOrder">
<omgdc:Bounds x="630" y="200" width="36" height="36"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="catchMaintenanceStart" id="BPMNShape_catchMaintenanceStart">
<omgdc:Bounds x="399" y="238" width="36" height="36"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="waitForMaintenanceEnd" id="BPMNShape_waitForMaintenanceEnd">
<omgdc:Bounds x="365" y="300" width="36" height="36"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flow_to_review" id="BPMNEdge_flow_to_review">
<omgdi:waypoint x="136" y="218"></omgdi:waypoint>
<omgdi:waypoint x="180" y="218"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow_to_sync" id="BPMNEdge_flow_to_sync">
<omgdi:waypoint x="285" y="218"></omgdi:waypoint>
<omgdi:waypoint x="330" y="218"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow_to_ship" id="BPMNEdge_flow_to_ship">
<omgdi:waypoint x="435" y="218"></omgdi:waypoint>
<omgdi:waypoint x="480" y="218"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow_to_end" id="BPMNEdge_flow_to_end">
<omgdi:waypoint x="585" y="218"></omgdi:waypoint>
<omgdi:waypoint x="630" y="218"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow_to_wait" id="BPMNEdge_flow_to_wait">
<omgdi:waypoint x="417" y="274"></omgdi:waypoint>
<omgdi:waypoint x="417" y="318"></omgdi:waypoint>
<omgdi:waypoint x="401" y="318"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow_to_retry" id="BPMNEdge_flow_to_retry">
<omgdi:waypoint x="365" y="318"></omgdi:waypoint>
<omgdi:waypoint x="300" y="318"></omgdi:waypoint>
<omgdi:waypoint x="300" y="218"></omgdi:waypoint>
<omgdi:waypoint x="330" y="218"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
文件二:IT系统维护流程 (itMaintenanceProcess.bpmn20.xml)
作用:这是一个辅助流程,由IT部门在需要维护时启动。它的唯一目的就是向整个系统广播开始和结束维护的信号。
核心逻辑:
流程开始后,立即通过一个中间信号抛出事件广播“维护开始”信号 (wmsMaintenanceStart)。
然后进入一个人工任务,代表IT人员正在进行实际的维护工作。
人工任务完成后,通过另一个中间信号抛出事件广播“维护结束”信号 (wmsMaintenanceEnd)。
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
targetNamespace="http://www.flowable.org/processdef">
<!--
定义此流程会抛出的信号。
-->
<signal id="wmsMaintenanceStartSignal" name="wmsMaintenanceStart"></signal>
<signal id="wmsMaintenanceEndSignal" name="wmsMaintenanceEnd"></signal>
<process id="itMaintenanceProcess" name="IT System Maintenance Process" isExecutable="true">
<startEvent id="startMaintenance" name="启动维护流程"></startEvent>
<sequenceFlow id="flow_m1" sourceRef="startMaintenance" targetRef="throwMaintenanceStart"></sequenceFlow>
<!-- 中间信号抛出事件:广播“维护开始” -->
<intermediateThrowEvent id="throwMaintenanceStart" name="广播维护开始信号">
<signalEventDefinition signalRef="wmsMaintenanceStartSignal"></signalEventDefinition>
</intermediateThrowEvent>
<sequenceFlow id="flow_m2" sourceRef="throwMaintenanceStart" targetRef="performMaintenanceTask"></sequenceFlow>
<userTask id="performMaintenanceTask" name="执行WMS维护工作" flowable:assignee="it_admin"></userTask>
<sequenceFlow id="flow_m3" sourceRef="performMaintenanceTask" targetRef="throwMaintenanceEnd"></sequenceFlow>
<!-- 中间信号抛出事件:广播“维护结束” -->
<intermediateThrowEvent id="throwMaintenanceEnd" name="广播维护结束信号">
<signalEventDefinition signalRef="wmsMaintenanceEndSignal"></signalEventDefinition>
</intermediateThrowEvent>
<sequenceFlow id="flow_m4" sourceRef="throwMaintenanceEnd" targetRef="endMaintenance"></sequenceFlow>
<endEvent id="endMaintenance" name="维护完成"></endEvent>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_itMaintenanceProcess">
<bpmndi:BPMNPlane bpmnElement="itMaintenanceProcess" id="BPMNPlane_itMaintenanceProcess">
<bpmndi:BPMNShape bpmnElement="startMaintenance" id="BPMNShape_startMaintenance">
<omgdc:Bounds x="100" y="100" width="36" height="36"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="throwMaintenanceStart" id="BPMNShape_throwMaintenanceStart">
<omgdc:Bounds x="180" y="100" width="36" height="36"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="performMaintenanceTask" id="BPMNShape_performMaintenanceTask">
<omgdc:Bounds x="260" y="83" width="105" height="70"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="throwMaintenanceEnd" id="BPMNShape_throwMaintenanceEnd">
<omgdc:Bounds x="410" y="100" width="36" height="36"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endMaintenance" id="BPMNShape_endMaintenance">
<omgdc:Bounds x="490" y="100" width="36" height="36"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flow_m1" id="BPMNEdge_flow_m1">
<omgdi:waypoint x="136" y="118"></omgdi:waypoint>
<omgdi:waypoint x="180" y="118"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow_m2" id="BPMNEdge_flow_m2">
<omgdi:waypoint x="216" y="118"></omgdi:waypoint>
<omgdi:waypoint x="260" y="118"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow_m3" id="BPMNEdge_flow_m3">
<omgdi:waypoint x="365" y="118"></omgdi:waypoint>
<omgdi:waypoint x="410" y="118"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow_m4" id="BPMNEdge_flow_m4">
<omgdi:waypoint x="446" y="118"></omgdi:waypoint>
<omgdi:waypoint x="490" y="118"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
第一个流程:订单履行流程 (orderFulfillmentProcess)
这个流程是你的核心业务流程,是公司里真正在“干活”的流程。
- 它的目的是什么?
它的目标很明确:完整地处理一个从客户那里来的订单,从创建、审核、分配库存直到最后发货。 - 它的正常执行路径(“快乐路径”)
一个订单的生命周期通常是这样的:
订单已创建 (Start):流程被触发。
审核订单 (User Task):可能需要人工审核一下订单信息是否合规。
与WMS同步分配库存 (Service Task):这是一个关键的自动化步骤,流程会自动调用仓库管理系统(WMS)的接口来锁定商品库存。
打包并安排发货 (User Task):库存分配成功后,通知仓库员工打包发货。
订单完成 (End):流程结束。 - 它的“智能”之处:如何处理意外情况?
这正是信号事件发挥作用的地方。我们预设了一种可能发生的意外:当我们正要和WMS系统同步时,IT部门突然要对WMS进行维护。
这个流程通过以下两个关键元素来优雅地处理这种情况:
边界信号事件 (catchMaintenanceStart)
它像什么? 就像一个附加在“分配库存”任务上的“警报接收器”。
它何时工作? 只有当流程执行到“分配库存”这一步时,这个警报接收器才被激活。
它做什么? 它一直“竖着耳朵听”一个名为 wmsMaintenanceStart 的全局广播。一旦听到,它会立刻做两件事:
中断当前正在进行的“分配库存”任务(cancelActivity=“true”)。
将流程的执行路径引向一条预设的“等待”路径。
中间信号捕获事件 (waitForMaintenanceEnd)
它像什么? 就像一个“等待室”。
它做什么? 流程进入这里后就会暂停,什么也不做,只是安静地等待。它在等什么呢?它在等另一个名为 wmsMaintenanceEnd 的全局广播。
唤醒后做什么? 一旦等到“维护结束”的信号,流程就会被唤醒,然后沿着预设的路线返回到“分配库存”任务,重新尝试一次。
第二个流程:IT系统维护流程 (itMaintenanceProcess)
这个流程是一个辅助流程或管理流程。它本身不产生业务价值,但它为核心业务流程的稳定运行提供了保障。 - 它的目的是什么?
它的唯一目的就是充当一个“广播站”,在IT部门进行系统维护时,向整个系统发出通知。 - 它的执行路径
这个流程非常简单直接:
启动维护流程 (Start):IT管理员决定开始维护,启动此流程。
广播维护开始信号 (Intermediate Throw Event):流程立即通过一个信号抛出事件,向外广播 wmsMaintenanceStart 信号。它不关心谁会收到,它只管喊话。
执行WMS维护工作 (User Task):这是一个分配给IT管理员的任务,代表他们正在进行的实际维护工作。流程会在这里等待他们完成。
广播维护结束信号 (Intermediate Throw Event):当IT管理员完成维护任务后,流程继续,并立即通过另一个信号抛出事件,向外广播 wmsMaintenanceEnd 信号,告诉所有人“维护结束了,系统恢复正常”。
维护完成 (End):流程结束。
两者如何协作:一个生动的剧本
想象一下这个场景:
10:00 AM: 客户A下单,一个“订单履行流程”实例(我们叫它订单A)启动,并顺利走到了“分配库存”这一步,开始与WMS系统通信。
10:01 AM: 客户B也下单了,另一个实例(订单B)也走到了“分配库存”这一步。
10:02 AM: IT部门的小王启动了“IT系统维护流程”。流程立刻广播了 wmsMaintenanceStart 信号。
信号的影响:
订单A的“警报接收器”听到了这个信号,立刻中断了与WMS的通信,流程被转向“等待室”。
订单B的“警报接收器”也听到了这个信号,同样中断任务,进入“等待室”。
此时,如果有新的订单进来,也会在走到“分配库存”时被中断并进入等待。
10:02 AM - 10:30 AM: 小王在埋头做系统维护。所有相关的订单流程都在“等待室”里安静地排队。
10:30 AM: 小王完成了维护工作,并在流程系统中点击“完成”了他的任务。
广播恢复信号: 小王的流程继续执行,立刻广播了 wmsMaintenanceEnd 信号。
集体唤醒:
订单A和订单B都在“等待室”里听到了这个“维护结束”的信号。
它们被同时唤醒,并自动返回到“分配库存”任务,重新开始执行。
总结
这个设计完美地体现了信号事件的精髓:
解耦 (Decoupling):订单流程不需要知道IT部门的存在,IT部门也不需要知道有多少订单在运行。它们通过一个约定好的“频道”(信号名称)进行通信。
广播 (Broadcast):一个信号可以同时影响多个正在运行的流程实例,实现了高效的全局协调。
健壮性 (Robustness):核心业务流程因此变得更加健壮,能够优雅地处理外部系统的临时不可用,而不是直接报错失败。