说说 jBPM 流程定义语言(12)—— 异步执行

jBPM 对于所有的流程执行操作默认都是同步的。所以我们调用 ExecutionService.startProcessInstanceByIdExecutionService.singalProcessInstanceById 之类的方法时,会让流程实例一直处于执行状态,直到流程实例到达一个 “等待” 状态后才会被中断。

同步执行之所以是默认的,是因为它有这些优点:
* 用户业务系统的事务可以很容易地接入 jBPM,这样 jBPM 的数据库持久化操作可以在业务系统的事务环境中完成。
* 当流程执行过程中,出现错误时,客户端可以立即获得一个异常。

一般情况下,在流程执行的两个等待状态之间,需要完成的工作都是一些耗时比较短的活动(自动活动),所以我们通常希望能够在一个事务中执行所有的这些工作。因此,jBPM 工作流引擎会在客户端发起的线程中同步执行流程的所有工作,直到陷入等待状态或流程结束。

jBPM 工作流引擎也支持异步执行。流程一旦进入异步执行,异步的消息就会被当做当前事务的一部分被发送出去,当前事务立即被提交;同时, 工作流引擎会为异步执行的活动启动一个新事务,最后,引擎会通过异步消息通知客户端,是否已完成异步执行工作(job)。

大多数活动都支持异步属性:

属性类型默认值是否必需描述
continuesync / async / exclusivesync可选异步属性

* sync:同步执行。
* async:异步执行,当前事务被自动提交,指定的活动会在一个新事务中执行。工作流引擎使用异步消息来通知我们是否已完成异步执行工作,异步消息会被包含在这个新事务中。
* exclusive| 独占模式的异步执行。当前事务也会被自动提交,指定的活动会在一个新事务中执行。。工作流引擎使用异步消息来通知我们是否已完成异步执行工作。这与 async 相同。不同的是,这个异步消息具有独占性,即工作流引擎会为每一个异步消息启动一个新的事务,以确保同一个流程实例中的每个异步工作都是绝对排他,非同时被执行。这样做可以避免资源互锁,但系统开销也会变大。在服务器集群环境中,这个属性很重要。

1 异步活动

设置异步活动的流程定义

jPDL:

<?xml version="1.0" encoding="UTF-8"?>

<process name="AsyncActivity" xmlns="http://jbpm.org/4.4/jpdl">
   <start g="224,202,48,48" name="start1">
      <transition to="生成 PDF"/>
   </start>
   <!-- 耗时活动 1(异步) -->
   <java class="net.deniro.jbpm.test.async.activity.Application" continue="async" g="348,199,103,52" method="generatePdf" name="生成 PDF">
      <transition to="计算"/>
   </java>
   <!-- 耗时活动 2(异步) -->
   <java class="net.deniro.jbpm.test.async.activity.Application" continue="async" g="513,200,120,52" method="calculatePrimes" name="计算">
      <transition to="end1"/>
   </java>
   <end g="682,204,7,8" name="end1"/>
</process>

假设这里有两个活动都很耗时,所以我们都把它们设置为异步方式执行。

现在模拟这两个耗时的活动:

jPDL:

public class Application implements Serializable {
    public void generatePdf() throws InterruptedException {
        Thread.sleep(1000);
    }

    public void calculatePrimes() throws InterruptedException {
        Thread.sleep(2000);
    }
}

注意:在生产环境中,job-executor (配置在 jbpm.jobexecutor.cfg.xml)会自动获得异步消息并执行它。这里为了测试方便,所以没有设置 job-executor,我们使用 managementService.executeJob(job.getId()); 来执行异步操作。

单元测试:

//发起实例
ProcessInstance processInstance = executionService.startProcessInstanceByKey
        ("AsyncActivity");
String pId = processInstance.getId();

//断言流程处于异步执行状态
assertEquals(Execution.STATE_ASYNC, processInstance.getState());

//获取异步消息队列中的第一条消息
Job job = managementService.createJobQuery().processInstanceId(pId).uniqueResult();

//手动执行
managementService.executeJob(job.getId());

processInstance = executionService.findProcessInstanceById(pId);

//断言流程处于异步执行状态(因为还有一条异步消息)
assertEquals(Execution.STATE_ASYNC, processInstance.getState());

//获取异步消息队列中的第二条消息
job = managementService.createJobQuery().processInstanceId(pId).uniqueResult();
managementService.executeJob(job.getId());

//断言流程已结束
assertNull(executionService.findProcessInstanceById(pId));

2 异步分支与聚合

异步分支与聚合的流程定义

在这个定义中,异步执行是并行执行的。

jPDL:

<?xml version="1.0" encoding="UTF-8"?>

<process name="AsyncFork" xmlns="http://jbpm.org/4.4/jpdl">
    <start g="164,210,48,48" name="start1">
        <transition to="fork1"/>
    </start>
    <fork g="280,212,48,48" name="fork1">
        <!-- 让并行的流程分支以独占的方式异步执行-->
        <on event="end" continue="exclusive"/>

        <transition g="301,187:" to="货物装船"/>
        <transition g="304,296:" to="汇款"/>
    </fork>
    <java class="net.deniro.jbpm.test.fork.Application" g="418,154,92,52" method="shipGoods"
          name="货物装船">
        <transition g="613,178:" to="join1"/>
    </java>
    <java class="net.deniro.jbpm.test.fork.Application" g="417,274,92,52" method="sendBill"
          name="汇款">
        <transition g="612,303:" to="join1"/>
    </java>

    <!-- 流程聚合-->
    <join g="589,226,48,48" name="join1">
        <transition to="end1"/>
    </join>
    <end g="700,225,48,48" name="end1"/>
</process>

我们在 fork 活动的 end 事件中,设置了独占异步属性,这将导致这两个分支指向的活动,都会使用独占的方式异步执行。

这两个 Job 对象将分别持久化,分别在各自的独立事务中执行相应的业务逻辑,然后聚合到 join 活动,在聚合活动时,两个事务可能会同时操作同一个流程执行实例(execution 对象),这可能会导致一个潜在的乐观锁失败问题。

单元测试:

//发起实例
ProcessInstance processInstance = executionService.startProcessInstanceByKey
        ("AsyncFork");
String pId = processInstance.getId();

//获取异步消息队列的消息列表
List<Job> jobs = managementService.createJobQuery().processInstanceId(pId).list();

//有两个分支
assertEquals(2, jobs.size());

Job job = jobs.get(0);

//手动执行其一
managementService.executeJob(job.getId());


//手动执行其二
job = jobs.get(1);
managementService.executeJob(job.getId());

//断言流程已结束
assertNull(executionService.findProcessInstanceById(pId));
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值