一种基于流程的高效引擎开发
2012-10-26王剑冰
王剑冰
一种基于流程的高效引擎开发
王剑冰
(北京福富软件技术股份有限公司福州分公司,福建,福州 350013)
在某些业务环境下,对实时的处理要求较高,需要由高效的流程引擎来带动的业务功能运行。高效流程引擎使用C/C++编写引擎内核,使用共享内存作为流程定义和流程实例等相关数据的缓存,并使用高效、交互性好、易扩展的Lua脚本作为流程引擎部分类型节点的嵌入式执行内容。同时,使用流程加载和日志落地等模块,使得持久层和缓存之间进行同步,不影响流程引擎核心的执行效率。通过以上各方面的优化提高了流程引擎整体的执行效率。
流程引擎;Lua脚本语言;C/C++;共享内存
业务流程管理一词通常是指企业通过一系列活动,以能够适应动态变化的环境的方式,自动管理及优化流程。业务流程可以被定义为一个由各种不同功能的活动相连的一组有相互关系的任务,它们依照一定的业务逻辑和顺序依次执行,业务流程有起点和终点,而且它们都是可以重复的。业务流程管理(BPM)正在迅速成为企业获得软件敏捷性和适应性的重要方法。近年来,基于流程引擎技术构建的应用系统越来越受到客户的追捧和认可,能否支持流程可定制、可更改、可运行的目标,也逐渐成为客户衡量一个应用系统主要标准之一[1]。目前国外有关流程引擎的产品有很多,如jBPM[2]、OSWorkflow、Apache ODE、WebSphere Process Server、Oracle Aqulogic、Microsoft BizTalk等。在国内,主要是在上述几个引擎基础上做扩展和应用,而且绝大部分是使用Java或C#实现,主要是因为电子商务的在流程引擎方面的应用较多,流程引擎中的节点调用的业务相对流程引擎本身的性能要慢得多,流程引擎的优化对整个流程的执行效率影响不明显[3],所以对于性能优化的侧重点一般不在流程引擎本身,但是,对于非电子商务的系统,尤其是某些对实时性要求较高的系统,如电信计费系统,业务网关等,流程引擎节点调用的业务执行速度很快,如果流程引擎不够优化,对整个流程执行的效率影响就比较大。流程引擎的主要功能是执行业务流程,是BPM的架构核心,其设计好坏、效率高低直接影响到整个BPM的性能高低。C/C++语言的主要特性就是高效,与操作系统底层交互良好,高效流程引擎应基于C/C++来编写[4]。
1 高效流程引擎设计
1.1 流程引擎总体架构
一个流程引擎包括很多模块,其内核主要是流程加载、流程执行和数据同步,提高流程引擎的性能,就必须提高流程引擎执行内核的效率,让流程的执行环境在内存中进行,所以架构中引入了缓存部分,设计专门的流程加载模块可以将配置数据向缓存中加载,同时还设计专门的数据同步模块能将缓存中执行结束的流程实例同步到持久层(数据库或文件),而流程引擎的使用者通过接口调用流程执行的时候只会在缓存中进行,故而能提高流程引擎的性能。高效流程引擎总体架构图如图1所示。
图1 高效流程引擎架构
从图1可以看出,业务流程必然和功能模块打交道,可以将一些功能模块编写成动态库,以便在流程中动态加载并被流程直接调用,模块化调用增强了执行的效率。另外为支持灵活性和维护的方便性,需要引入一些即时生效的脚本作为节点的执行内容。从执行效率以及接口效率来评估,使用C/C++语言作为流程引擎核心的编写语言,而采用和C/C++语言交互性好且执行效率很高的脚本语言Lua,是比较合适的方案。
1.2 流程定义
流程引擎的设计是面向图的设计,流程是由两个最基本的元素组成:“节点”及“有向连接”。对于“有向连接”几乎没有任何歧义,所有的流程建模描述中“有向连接”都是存在“From”和“To”这两个特性。但是对于“节点”,则因为所处的视角、功能不同,则存在很多不同的理解,比如WFMC的过程定义元模型、jBPM、EPC中对节点含义和种类的定义都不太相同[5]。高效流程引擎中的节点类型包含:开始、结束、函数、脚本、状态、分支(并行、判断)、汇聚等,如图2所示。
图2 节点连接图
各节点既有共性也有个性,从流程定义的角度,将节点抽象为父类,各种类型的节点继承节点父类,并增加自己的属性、函数,以及实现节点执行函数。各节点的类图关系如图3所示。
图3 节点类关系
将节点类设计为一个抽象类,它包含了纯虚函数virtual execute()=0,在执行流程时以父类对象指针指向最终节点类的对象,并调用父类对象指针的execute函数,则实际会调用最终子类中的所实现的execute成员函数。在流程引擎中我们去除了有向线连接对象,即jBPM中的Transition,使用每个节点的前驱或后续节点号的方式来贯穿整个流程。任何流程的开始节点号都定义为0。
1.3 流程解析与加载
前台配置的流程的定义是存储在数据库中的,一般的流程引擎的做法是每次连续的执行都要到数据库中去读取流程配置,这样势必造成I/O的增加,降低流程引擎的性能。由于流程定义的读频率要远远高于写频率,所以,要保证流程的高速运转,必须将流程定义加载到内存中,并且解决好读写的冲突问题。
流程定义缓存主要分为共享内存和私有内存两部分,共享内存是流程定义索引区和流程定义区,这部分可以由所有调用者的程序共享读取;私有内存是脚本加载区和动态库加载区,在程序启动的时候需加载和初始化。流程索引是根据流程定义ID以及流程的版本号查找流程定义存放的地址的区域,为了加速流程定义的查找速度。流程定义使用xml格式配置,调用rapidxml[6]开源程序中的函数进行解析,并将配置按类的定义以序列化方式保存在共享内存的流程定义中。
由于流程的执行是一个持续的过程,在流程实例未结束之前,改流程实例使用的流程配置都应该保持在内存中且不能被改动。对于新增的流程,在流程定义区新开辟空间并上载新流程定义,上载完成后锁定流程定义索引区,更新Hash(流程定义ID,流程版本)和流程定义存放地址的对应关系,然后解锁;对于要删除的流程,一般是将该流程打上删除标志而不是立即删除,由流程上载模块负责查询没有使用该流程定义的流程实例后将其删除并回收空间;对于修改的流程,采用升级的方式处理,也就是先将新的流程加载到新开辟的流程区,并更新流程定义索引区,新的流程实例会按新版本执行,当使用旧版本的流程实例全部结束后,流程上载模块会将旧版本的流程定义删除并回收空间。通过这样的增、删、改的机制可以解决流程定义读写的冲突。
1.4 流程脚本
脚本具有灵活性强、能立即生效等特点,在流程引擎的节点中使用脚本可以带来灵活性和扩展性和高效性。Lua脚本语言是用标准C编写而成的嵌入式脚本语言和C/C++有良好的交互性,能为应用程序提供灵活的扩展和定制功能,而且几乎在所有操作系统和平台上都可以编译和运行。Lua是脚本语言中执行效率最高且很轻量级,内存占用很少[7]。
经过对相同的浮点数运算程序测试,以C语言(编译器gcc 4.0.1)为基准,进行速度测试,可得出如图4的各种脚本语言执行效率的对比数据。
图4 脚本语言效率对比
Lua和C语言的交互,一般是Lua嵌入到C/C++程序中,简单的功能可以直接由Lua函数提供,如果Lua完成不了的,也可以编写可由Lua调用的C/C++接口函数,让Lua起到桥梁作用,快速重组这些接口函数。
1.5 流程监控
流程的执行过程在后台进行,流程引擎需要提供对流程实例执行情况的监控。流程实例中的节点状态、执行时间、流程变量值等的都在共享内存区中可以通过IPC进行访问,流程引擎为监控提供了一系列查询接口供同主机的外部进程访问,并且可以在此基础上封装成监控服务,供远程主机访问和显示。由于在共享内存中进行查询开销较少,所以可以提供较为实时的监控。
1.6 流程日志
一个流程实例执行结束的流程引擎会产生一个消息存放在队列中,由入库模块将流程实例执行的成功与否以及各节点执行情况和时间保存到数据库,相当于记录了流程的执行轨迹,并将该流程实例从共享内存中的空间释放出来。
流程入库程序可以是多线程并行执行,并且是在流程结束后才进行,而且实例与监控区的共享内存空间较大,可以是对多个流程的批量处理,所以如果配置合适,并且流程实例的执行压力不是特别大的情况,基本不会形成性能瓶颈。关闭流程引擎将共享内存中的所有数据落地到文件,重新启动时则从文件上载到共享内存,如果主机在流程实例未执行完成的时候出现崩溃,则因共享内存数据失去,流程实例数据将会丢失,这是一种缺陷,但这是流程执行效率和数据安全之间的一种权衡的结果。
2 测试与分析
2.1 测试环境
该测试的实验环境是:操作系统AIX 6.1,主机CPU 3.5GHZ,主机内存8G的台式机;开发工具为Java编译器Java1.6,C++编译器xlC 。
2.2 测试数据
在jBPM和高效率流程引擎都配置一个相同的流程定义如图5。其中节点1脚本节点,功能是设置一个整数型的流程变量值。节点2为判断节点,功能是将变量值取出,并将其乘以1.5加0.01,判断结果是否大于3。节点3为业务功能节点,操作是对变量进行500次浮点加减乘除运算。节点4为状态节点,无操作。
图5 测试流程
2.3 测试结果
分别编写Java程序和C++程序对3.2中的准备的完全相同的流程定义调用两种流程引擎(jBPM和高效流程引擎)进行流程定义加载、流程实例创建和实例执行的速度测试。各进行5组测试。流程定义加载效率测试和流程实例执行测试的结果如表1所示。
表1 流程引擎加载与流程实例执行测试
从表1测试结果可以得出,高效流程引擎的效率远远高于jBPM,分析其主要原因是jBPM的流程加载和流程执行的每一步都对数据库进行了操作,增加了大量的I/O开销,而高效流程引擎使用共享内存;并且jBPM使用的是Java语言,本身执行效率上不如高效流程引擎使用的C/C++语言效率高。该测试数据的结果也表明可满足大多数系统应用中对流程引擎的性能要求。
3 结束语
在某些业务要求下,流程引擎除了注重功能还需注重性能。C/C++语言是当前执行效率最高的语言[8],Lua脚本是效率最高的脚本,且和C/C++语言的交互性以及自身扩展性都很强,高效流程引擎使用C++作为引擎核心的编写语言,使用Lua作为脚本节点以及判断节点的应用,从程序执行方面对性能进行了提高。流程引擎在执行过程中对流程定义的读取以及日志的保存如果都使用数据库或者文件,会给性能带来较大瓶颈,所以高效流程引擎使用共享内存作为流程定义、流程实例、流程日志等的数据缓存,并使用其他模块来对流程定义进行加载和日志的落地。高效流程引擎从各个方面提高了流程引擎的性能,经过测试达到了业务系统对性能的要求。
[1] 胡长城. 工作流的微内核架构[EB/OL].http://gocom. primeton.com/modules/gSpace/modules/techresource/article1803.htm, 2007-9-7.
[2] 胡长城. 揭秘jbpm流程引擎内核设计思想及构架[EB/OL].http://www.uml.org.cn/workclass/200709305.asp, 2007-9-22.
[3] 何智华,王力生. 基于Web服务的电子商务业务流程中间件引擎的研究[J].计算机应用, 2004(11):135-138.
[4] 陈兰新. 基于C++的BPEL流程引擎原型的设计与实现[D]. 广州:华南理工大学,2009.
[5] 胡奇. jBPM4工作流应用开发指南[M].北京:电子工业出版社,2010.
[6] Marcin Kalicinski. RAPIDXML Manual[EB/OL].http:// rapidxml.sourceforge.net/manual.html, 2009-8-2.
[7] Roberto Ierusalimschy. Programming in Lua[EB/OL]. http://www.lua.org/pil/, 2012-3-4
[8] 卢晓苗,李从龙,张建明. 一例Java语言与C语言代码运行效率的比较[J]. 现代计算机(专业版), 2010(1): 116-118.
Development of High-performance and Flexible Process Engine
WANG Jian-bing
(Beijing Forich Software Technology Co., Ltd. Fuzhou Branch, Fuzhou , Fujiang 350013, China)
High real-time processing is required in some business environments, and then the high-performance process engine is needed to drive the business functions running. The core of high-performance process engine is written by C/C++, shared memory is used as a data cache for process definition and process instance, and the Lua script, which is efficient, interactive, easy to extend, is embedded in the execution content of some nodes of process engine. At the same time, the module of process loading and log storage synchronize the persistence layer and cache, the core of process engine is not affected. Through the optimization of the above aspects, the overall performance of the process engine is improved.
process engine; Lua script language; C/C++; share memory
TP391
A
10.3969/j.issn.1674-8085.2012.04.014
1674-8085(2012)04-0061-05
2012-02-24;
2012-03-27
王剑冰(1975-),男,福建浦城人,工程师,主要从事引擎开发、数据库优化研究(E-mail: wangjb@ffcs.cn).