第 10 章 异步约定
10.1. Concept 复制链接链接已复制到粘贴板!
复制链接链接已复制到粘贴板!
Dan 基于 Graph-Oriented 编程 (GOP)。基本上,GOP 指定一个简单的状态计算机,它可以处理执行的并发路径,但在指定的执行算法中,所有状态转换都会在单个线程客户端操作中进行。默认情况下,这是在客户端的线程中执行状态转换的好方法,因为它适用于服务器端事务。进程执行从一个"等待"状态移到一个事务空间中的另一个状态。
在某些情况下,开发人员可能希望微调进程定义中的事务。在 jPDL 中,可以指定进程执行应该与属性
async="true"
异步继续。async="true"
仅在事件中触发但可在所有节点类型和所有操作类型中指定时才支持。
10.2. 示例 复制链接链接已复制到粘贴板!
复制链接链接已复制到粘贴板!
通常,节点总是在令牌输入后执行。因此,节点会在客户端的线程中执行。一个将通过查看两个示例来探索异步持续情况。第一个示例是具有三个节点的进程的一部分。节点 'a' 是一个等待状态,节点 'b' 是一个自动步骤,节点 'c' 是等待状态。这个过程不包含任何异步行为,在下图中表示。
第一个帧显示启动情况。令牌指向节点 'a',这意味着执行的路径正在等待外部触发器。该触发器必须通过向令牌发送信号来提供。当信号到达节点时,令牌将通过转换到节点"b"从节点"a"传递。在令牌到达节点 'b' 后,执行节点 'b'。重试该节点 'b' 是一个自动步骤,它不充当等待状态(例如发送电子邮件)。因此,第二个帧是在执行节点"b"时进行的快照。由于节点 'b' 是进程中的一个自动步骤,节点 'b' 的执行将包括向节点"c"传播令牌。节点 'c' 是一个等待状态,因此第三个帧在信号方法返回后会显示最终情况。
图 10.1. 示例 1:没有异步协调的进程
Whilst "persistence" 并不强制使用,通常在事务中调用信号。查看该事务的更新。最初,令牌被更新以指向节点 'c'。这些更新由 Hibernate 在 JDBC 连接上生成,因为
GraphSession.saveProcessInstance
。其次,如果自动操作访问和更新某些事务资源,则此类更新应合并或在同一事务的一部分。
第二个示例是第一个变体,并在节点 'b' 中引入了异步延续。节点"a"和"c"的行为与第一个示例的行为相同,即它们的行为与等待状态相同。在 jPDL 中,通过设置属性
async="true"
来标记为异步节点。
将
async="true"
添加到节点 'b' 的结果是,进程执行将分为两个部分。其中的第一个将执行进程,直到要执行节点"b"的时间。第二部分将执行节点 'b'。 该执行将停止等待状态 'c'。
因此,事务将分成两个独立的事务,每个事务对应一个。虽然它需要一个外部触发器(调用
Token.signal
方法)来离开第一个事务中的节点"a",但 lsblk 将自动触发并执行第二个事务。
图 10.2. 具有异步一致性的进程
对于操作,原则类似于。带有属性
async="true"
的操作是在执行进程的线程外执行的。如果配置了持久性(默认为 ),则操作将在单独的事务中执行。
在 Dan 中,异步持续使用异步消息传递系统来实现。当进程执行过程在应异步执行的时间到达时,Chef 将挂起执行,生成命令消息并将其发送到命令执行器。命令 executor 是一个单独的组件,在收到消息时,将恢复暂停进程的执行。
xmvn 可以配置为使用 JMS 提供程序或内置的异步消息传递系统。内置的消息传递系统在功能方面有限,但允许在 JMS 不可用的环境中支持此功能。
10.3. 作业执行者 复制链接链接已复制到粘贴板!
复制链接链接已复制到粘贴板!
作业执行程序是 异步恢复进程执行的组件。它会等待作业消息到达异步消息传递系统并执行它们。用于异步延续的两个作业消息是
ExecuteNodeJob
和 ExecuteActionJob
。
这些作业消息由进程执行生成。在进程执行期间,对于必须异步执行的每个节点或操作,
作业
(旧 Java 对象)将被分配给 MessageService
。消息服务与 JbpmContext
关联,它只收集必须发送的所有消息。
消息将作为
JbpmContext.close ()
的一部分发送。此方法会对所有相关服务的 close()
调用进行级联。实际的服务可以在 jbpm.cfg.xml
中配置。默认情况下,其中一个服务 JmsMessageService
配置,并通知作业执行器新的作业消息可用。
图形执行机制使用接口
MessageServiceFactory
和 MessageService
来发送消息。这是使异步消息服务可配置(也位于 jbpm.cfg.xml
中)。在 Java EE 环境中,DbMessageService
可以替换为 JmsMessageService
,以利用应用服务器的功能。
以下是作业执行器工作方式的简短总结。
"jobs"是数据库中的记录。此外,它们是对象,可以执行。计时器和异步消息都是作业。对于异步信息,dueDate 只需在插入时将其设置为当前时间。作业执行器必须执行该作业。这分为两个阶段。
- 分配程序线程必须获得一个作业
- executor 线程必须执行该作业
获取作业并执行作业是在 2 个独立的事务中进行的。分配程序线程代表此节点上的所有 executor 线程从数据库获取作业。当 executor 线程使用作业时,它会将其名称添加到作业的 owner 字段中。每个线程都有一个基于 IP 地址和序列号的唯一名称。
线程可能会在作业参与和执行之间失败。要在这些情况后进行清理,每个作业执行器都有一个锁定的线程来检查锁定时间。锁定监控线程将解锁任何已锁定超过 10 分钟的作业,以便它们可以由另一个作业执行器线程执行。
隔离级别必须设置为
REPEATABLE_READ
,以便 Hibernate 的选择锁定正常工作。REPEATABLE_READ
保证此查询只会更新其中一个销售事务中的一行。
update JBPM_JOB job set job.version = 2 job.lockOwner = '192.168.1.3:2' where job.version = 1
update JBPM_JOB job
set job.version = 2
job.lockOwner = '192.168.1.3:2'
where
job.version = 1
不可重复读取可能会导致以下异常:事务重新读取之前已读取的数据,并发现数据已被另一个事务修改,它是自事务之前读取以来提交的事务。
不可重复读取是最佳锁定的问题,因此隔离级别
READ_COMMITTED
不足,因为它允许出现不可恢复的读取。因此,如果您配置多个作业执行器线程,则需要 REPEATABLE_READ
。
与作业执行器相关的配置属性有:
- jbpmConfiguration
- 从中检索配置的 bean。
- name
- 此 executor 的名称。重要当在一台机器上启动多个 实例时,此名称对于每个节点都是唯一的。
- nbrOfThreads
- 启动的 executor 线程数量。
- idleInterval
- 如果没有任务待处理,在检查作业队列前,分配程序线程将等待的时间。注意当作业添加到队列中时,分配程序线程会自动通知。
- retryInterval
- 如果在执行过程中失败,作业会在重试之间等待的时间。此值的默认值为 3 次。注意重试的最大次数由 jbpm.job.retries 配置。
- maxIdleInterval
- idleInterval 的最大周期。
- historyMaxSize
- 此属性已弃用,无效。
- maxLockTime
- 在 lock-monitor 线程解锁前可以锁定作业的最长时间。
- lockMonitorInterval
- lock-monitor 线程在检查锁定的作业之间的休眠时间。
- lockBufferTime
- 此属性已弃用,且不会影响。
10.4. Ice 的内置异步消息传递 复制链接链接已复制到粘贴板!
复制链接链接已复制到粘贴板!
在使用 lsblk 的内置异步消息时,将把作业消息保留给数据库来发送。此消息持久可以在与 jBPM 进程更新相同的事务或 JDBC 连接中完成。
作业消息将存储在 J114
_JOB
表中。
POJO 命令 executor (
org.jbpm.msg.commandExecutor
)将从数据库表中读取消息并执行它们。POJO 命令执行器的典型事务类似如下:
- 读取下一个命令消息
- 执行命令消息
- 删除命令消息
如果执行命令消息失败,事务将回滚。之后,将启动一个新的事务,将错误消息添加到数据库中的消息。命令 executor 过滤掉包含异常的所有消息。
图 10.3. POJO 命令执行器事务
如果向命令消息添加异常的事务失败,则会回滚。该消息将保留在队列中,没有例外,之后将被重试。
重要
Ice 的内置异步消息传递系统不支持多节点锁定。您不能多次部署 POJO 命令执行器,并将其配置为使用同一数据库。