Apache ODE流程运行过程解析

流程运行概述

流程运行需要引擎首先创建该流程的实例,当然实例的创建也是以接收用户发送的调用消息起始的,然后根据该流程对象的定义按顺序执行一个个活动,像赋值操作、等待操作等,当然最重要的还有调用外部服务的操作,同时还要负责接收发送回来的响应消息,根据响应消息的内容再执行后续的流程,最后流程终止后,销毁该实例。


 SOAP消息的接收与传递 

在本系统中与外界的所有通信都由AXIS2来负责,通过其配置文件来指定不同的消息类型如何进行处理,比如可以给所有的进入消息配置一个处理器,该处理器的工作就是检查该消息头部,得知其是否有状态,以及是否需要返回消息等。通过这样的实现方式就可以对所有接受到的消息进行一次前处理,方便后续的操作。同样,我们可以配置多个处理器,针对各种不同的操作都有相应的动作。这种通过配置文件就可以改变消息处理方式的做法非常简单,这也说明AXIS2的设计扩展性非常好,可以很方便的与其他系统进行集成。

在上一节中说到流程编译的最后一个步骤是将流程暴露为Web服务,同时将该服务所有操作(Operation)都绑定一个消息接收器,这里的消息接收器就可以认为是引擎与AXIS2之间的桥梁,通过它来将AXIS2接收到的消息转发到引擎内部。至于AXIS2是如何接收客户端发送来的SOAP消息并不是本论文讨论的重点,这个内容可以查看相关AXIS2的文档。还应注意到的一点是,在引擎系统中所有的消息可以分为两种,一种是由外部客户端发送过来的,而另一种是引擎内部自己作为流程执行所需而传递的消息。对于外部消息可以采用AXIS2系统中的MessageConext类作为消息表示,这就省去了消息格式转化的步骤,但是这种类型消息只能使用在从外部接收到的内容,因为它并不包含下一步的路由信息,不适用于引擎内部进行交互。在流程执行过程中,所有的消息应该采用内部封装的格式,利用事件触发的形式进行流程的执行。

在本系统中我们为AXIS2配置了四个消息处理器,如图,其中第一个消息进入处理器是对所有接收到的SOAP消息进行一个前期处理,来判断此次调用是有状态的,还是无状态的,以及是否需要返回消息。首先判断此次消息调用头部信息中是否已经有了session标识和所要调用的endpoint信息,则将这些消息取出,以属性的形式加入到AXIS2所接收的MessageContext中,同样的,对于是否需要返回消息,也采用此办法。这样,在后续的操作中所有的此次调用信息都可以通过提取MessageContext中的属性来获得,比较方便,不用每次都从消息头部中解析。第二个查找服务处理器,是根据请求地址URL中的服务名称来查找的。在本系统中,对外暴露的服务所调用的地址模式是/processes/service-a这种形式,通过解析该字符串拿到service-a这样的服务名称,这样便可以从服务注册处(系统内部保持所有已提供服务的信息)拿到服务对象。第三个查找操作处理器是根据上一步中得到的服务对象和消息内容来确定调用的是哪个操作(operation)。这个操作的句柄可以很容易获得,而这样的操作在流程编译阶段就已经都绑定了固定的消息接收器,当AXIS2对该operation进行调用时,其所传递给该操作的入口参数(消息)也就是传递给了提前注册好的消息接收器。在本系统中,所有由AXIS2转发的消息都由同一个接收器进行拦截,在该接收器中判断此次调用是否需要回应,然后再调用业务逻辑。至此,消息的接收和转发就到此为止了。


 流程实例的创建

 

根据接收到的消息可以创建一个新的流程实例,有了实例才可以进行流程运行。第一步需要将接收到的消息转化为内部表示形式,转化的过程一个最重要的步骤就是为该消息加上唯一的UUID,这个标识将作为后续多步操作的一个关联子,通过它来将后续的操作串接起来,不至于多个消息到达后无法判断应该发往哪个实例。还有,在消息转化的过程中加入了该消息所对应的流程信息,因为这时候的消息是启动流程实例,其中并没有带有任何关联子,系统无法判断应该将其路由到哪里,所以必须为其指定流程信息从而可以创建流程实例。



 对于引擎来说需要同时处理各个不同客户端发送的消息,在同时部署多个流程,多个流程实例在运行时会造成系统很大的负载,从而影响性能。在这里我们采用事件机制,引擎处理流程执行的动作都将其封装为一个任务(Job),通过在一个线程中不断检查可执行任务队列来执行一个个任务。在准备好消息之后,将消息通过任务数据类型封装添加到事件队列中,当任务从队列中弹出时,就是执行该任务了。而任务的执行者需要根据任务的类型来判断如何运行该任务,不同类型的任务交给不同的对象去处理。

 

流程实例的创建,就是要在内存中创建一个该流程运行的上下文环境,让该流程所涉及的各个活动能够在当前环境下运行,这个环境包括了初始消息、编译后的流程定义、该流程的关联子以及与该流程实例相关的变量等。这个包括了流程运行上下文环境的对象就是一个实例,新建该对象只是准备好了所有需要的信息,还需要启动该实例。启动实例实际的操作就是调用该流程的第一个活动,成功调用流程的第一个活动之后流程实例的创建就算完成了。

流程实例执行过程

 

实例的运行需要结合流程定义文件,而BPEL定义文件是可以分为各种不同的标签,代表了不同的语义,同时也就是对应了不同的操作。针对每个不同的标签设计其运行方式,例如BPEL定义文件中最外层的PROCESS标签就有自己特定的执行方式,它主要是来获得其下层的子标签,并启动其子标签的执行。每个不同的标签都有自己的执行方法,但同时各个标签之间都有着各种联系,像父标签与子标签的关系,并行关系等。

易于想到的流程执行实现方法就是,从流程定义的最外层标签一层一层的进行调用,根据流程定义的条件判断执行路径,不断的创建新的活动,并且调用它。这是最容易实现也最直观的一种方式,但是它却存在诸多问题。首先,这种方式占用的内存空间很可能非常大。因为父标签所对应的对象占用的空间,只有在其所有子标签都执行完毕之后才可以得到释放,这样它才能向它自己的父标签报告执行结果,而如果流程嵌套的很深,其最外层的标签将很长时间得不到释放。而且一般实际生产过程中,一个实际的业务流程涉及到的操作节点很多,逻辑也十分复杂,对应到BPEL流程定义文件就是其所包含的标签很多,其执行时间可能一两天甚至几个月都有可能,那么这种情况下对于系统的性能影响将是非常之大的。其次,在流程运行过程中,如果需要对流程执行状态进行持久化(用户执行流程的挂起操作等情况),那么就需要将当前执行线程调用堆栈中的所有对象弹出,同时对其执行状态持久化存储。这必然需要操作对象(也就是一个个标签的实现类)的支持,这个需求将会大大增加每个标签操作类的设计难度和复杂度。

因此我们在流程执行的设计中,将流程不同标签的逻辑操作与标签之间的通信操作相分离,标签的实现类只负责其逻辑操作,调用子标签和向父标签告知其执行结果的工作不需要由当前标签来完成,而重新建立一个专门的流程活动处理器,它建立两个执行队列(在这里虽然叫做队列,但并不保证其调用顺序),一个保存需要执行的活动,我们称其为操作队列;另一个保存各个活动执行结束之后的回调操作,叫做回调队列。如果该活动在执行结束后需要告知其父活动,那么在进入队列的时候就需要提前将这标签活动与回调操作进行绑定,也就是将这两个调用进行关联,那么在执行队列调用到该标签操作时就可以正确的调用其事先指定的方法。在这里标签活动的队列是一个主动查找的队列,也就是在流程开始执行后,该队列需要被不断的检查,一发现有可以执行的操作就调用,而另一个回调操作队列则是被动查找的,因为在每一个需要回调的活动进入队列的时候就将一个与其对应的回调操作加入到了该队列中,所以随着该活动的被弹出执行,其回调方法也会被查找到进行执行。



 该模型就相当于一个生产者消费者模式,各个标签定义的操作就是生产者,同时也是产品,队列就相当于是缓冲池,流程活动处理器就是消费者。它三者之间的关系就是,操作的执行会将其子标签的操作加入到队列中,而流程活动处理器在不断的检查该缓冲池中是否有可用的产品,一旦发现有了可执行的操作就将其弹出执行,而同时这个弹出的操作有可能将新的操作加入到队列中。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值