Activiti流程定义缓存源码分析1

    1. 背景

       何为缓存,缓存就是将系统或者程序中经常使用但不频繁更新的数据(或者对象)放置到内存中,以便其使用时可以进行快速地查找,而不必再去从数据库(或者其它介质)中获取数据或者创建新的重复实例操作,这样做的好处就是可以减少系统开销,提高系统运行效率。缓存在项目中占据很大的比重,如何合理的运用缓存往往决定一个系统的性能瓶颈。目前市面上比较成熟的第三方缓存框架有Redis、Memcached 等等。之前多次提到过流程引擎在部署流程文档完毕之后,会将流程虚拟机中的对象放置到缓存中,这样流程虚拟机运转的时候可以直接从缓存中取出该对象,而无需再次对流程文档进行解析转换操作,从而大大提升流程实例的运转效率。Activiti框架中哪些地方使用到了缓存呢?缓存的意义何在呢?这些问题都需要我们认真思考。

通常情况下Activiti引擎以一个无状态的方式工作,所有关于流程实例的状态、任务以及其它归档信息均存储在数据库,只有在必要的时候流程引擎才会去查询数据库(比如完成任务),一个流程实例通常可以“休眠”很长一段时间,因此运行中的流程实例不会占据太多的系统资源。这样的设计有如下两个好处。

  1. 它使Activiti的内存占用低,只有需要获取流程实例信息或者历史任务等操作才会查询数据库。
  2. 它允许在一个集群的环境下(共享数据库)运行Activiti,当然了集群中的节点相互之间不需要通讯,因为数据库才是唯一的真实数据来源,当集群中的任意一个节点需要获取一些流程的状态,它可以直接从数据库中进行取出或者写入。

以上规则有一个例外,在Activiti引擎执行操作期间,MyBatis框架(底层使用的ORM框架)会保持一个“会话缓存”来避免在一个事务中多次从数据库中获取相同的数据(比如主键)。然而这个“会话缓存”生命周期是非常短暂的,用朝生夕死来形容也不为过,因此该“会话缓存”不会危及集群上运行Activiti的能力。可以查看之前的章节进行学习。

上面提到了Activiti工作方式是无状态的,可能读者会想为什么是无状态的呢?其实也很简单,通常情况下Activiti引擎运行在Web容器(比如Tomcat、Weblogic等)并通过Http方式提供服务,由于Http协议是无状态的,因此可以说Activiti操作也是无状态的,当然了Activiti自身也不会记录上次是谁操作的,操作的什么数据。客户端操作Activiti的时候,才会跟Activiti建立一个短连接,该操作执行完毕之后,连接立刻被关闭,相应的资源也会被回收。由于缓存所有的流程数据可能非常占用系统资源和内存,通常我们期望将永远不会改变的数据作为优先考虑的缓存对象,比如流程定义文档。客户端提供流程定义文档XML文件,流程引擎解析流程文档的元素并将其转化为可以执行的结构(更具体地说:就是将流程定义文档解析转化为一个Activiti内部Pojo树),以上解析工作完毕之后,将流程文档的内容以及必要信息存储到数据库。比如描述信息、业务键等等。以上过程产生的流程定义文档永远不会改变(除非人为进行修改),一旦存储到数据库中,存储的数据将保持不变,直到流程定义删除。上面所说的过程即解析流程文档的过程,该过程是非常消耗系统资源的(XML解析总是),因此Activiti引擎在内部使用缓存来存储流程文档解析之后的结果如图x-所示,这样当启动流程实例、完成任务等操作的时候首先尝试从缓存进行加载,如果缓存中没有值,则开始查询数据库并再次执行流程文档的解析,最终将解析结果存储到缓存中。

    1. 缓存策略初始化

对于流程部署管理器DeploymentManager类的职责,相信读者应该还有一定的印象,我们再次以该类为切入点,深入探究一下Activiti中缓存的应用场景。该类的相关代码如代码清单x-所示。

代码清单x-DeploymentManager.java

---------------------------------------------------------------------------------------------------------------------------

 DeploymentCache<ProcessDefinitionEntity> processDefinitionCache; #-1

  DeploymentCache<BpmnModel> bpmnModelCache; #-2

  ProcessDefinitionInfoCache processDefinitionInfoCache; #-3

  DeploymentCache<Object> knowledgeBaseCache; #-4

---------------------------------------------------------------------------------------------------------------------------

上面的代码中,#-1负责流程定义实体对象的缓存,#-2负责BpmnModel实例对象的缓存,#-3负责流程定义附属信息的缓存,#-4负责创建一个KnowledgeBase的实例并且放入缓存(RulesDeployer使用)。#-1、#-2和#-4的实数据类型均为DeploymentCache。#-3数据类型为ProcessDefinitionInfoCache。既然DeploymentManager类负责管理所有的缓存对象,那我们再次回顾一下该类的初始化过程,进而探究一下缓存类的实例化过程。相关核心代码如代码清单x-所示。

代码清单x-ProcessEngineConfigurationImpl.java

---------------------------------------------------------------------------------------------------------------------------

protected void initDeployers() {

    if (deploymentManager==null) {//用户可以自定义部署管理器

      deploymentManager = new DeploymentManager();

      deploymentManager.setDeployers(deployers);

      if (processDefinitionCache == null) {

        if (processDefinitionCacheLimit <= 0) { #-1

    processDefinitionCache = new DefaultDeploymentCache<ProcessDefinitionEntity>();

        } else {

processDefinitionCache=new DefaultDeploymentCache<ProcessDefinitionEntity>(processDefinitionCacheLimit);

        }

      }

      if (bpmnModelCache == null) { #-2

        if (bpmnModelCacheLimit <= 0) {

          bpmnModelCache = new DefaultDeploymentCache<BpmnModel>();

        } else {

bpmnModelCache = new DefaultDeploymentCache<BpmnModel>(bpmnModelCacheLimit);

        }

      }

      if (processDefinitionInfoCache == null) { #-3

        if (processDefinitionInfoCacheLimit <= 0) {

processDefinitionInfoCache = new ProcessDefinitionInfoCache(commandExecutor);

        } else {

processDefinitionInfoCache = new ProcessDefinitionInfoCache(commandExecutor, processDefinitionInfoCacheLimit);

        }

      }

if (knowledgeBaseCache == null) { #-4

        if (knowledgeBaseCacheLimit <= 0) {

          knowledgeBaseCache = new DefaultDeploymentCache<Object>();

        } else {

 knowledgeBaseCache = new DefaultDeploymentCache<Object>(knowledgeBaseCacheLimit);

        }

      }

    }

  }

---------------------------------------------------------------------------------------------------------------------------

上面代码的执行逻辑,之前的章节也详细地讲解过,我们暂且将关注点放到缓存策略类的实例化操作中,以上四种缓存的处理虽然缓存对象不同,但是处理流程大体相同。我们可以将其处理逻辑进行梳理,并进行如下总结。

  1. 客户端自定义缓存类判断。

以上四种缓存执行类的处理,首先判断客户端是否自定义了相关缓存的处理类,如果客户端自定义了缓存处理类,则优先使用。否则使用系统内置的缓存处理类。

  1. 系统内置缓存类策略判断。

首先获取客户端对需要缓存对象的容器大小限制值。以上四种缓存限制值默认为-1,也就是说默认对缓存对象的容器大小不进行限制,其内部直接使用Hashmap进行实现。当缓存容器大小有限制的时候,则使用LRU算法控制容器的大小。程序以缓存限制值为分水岭执行不同的逻辑,不管缓存限制值的大小为多少,都会根据缓存限制值实例化不同的缓存处理类。#-3实例化ProcessDefinitionInfoCache类型的处理类,#-1、#-2和#-4分别实例化DefaultDeploymentCache类

技术团队支持:盘古BPM工作流平台

具体效果参考盘古BPM

发布了206 篇原创文章 · 获赞 580 · 访问量 177万+

猜你喜欢

转载自blog.csdn.net/qq_30739519/article/details/104160353