对象池在构造高性能工作流产品中的应用研究
来源:赛迪网 更新时间:2012-04-15

随着我国信息化程度的不断提高,工作流技术在各个行业的信息系统中都得到了广泛的应用。基于工作流技术发展而来的工作流管理系统也越来越成为一种重要的基础中间件软件产品。电子政务、电信、物流等各种行业领域,都在基于工作流平台之上开发其各自领域的应用系统。这些基于工作流的应用系统,为各自领域的企事业单位进行流程自动化、流程再造等发挥了重要作用。

但随着信息化的不断深入,电信业务的不断扩大,信息系统用户数激增,对基于工作流的应用系统性能提出了更高的要求,同时也就对工作流平台产品提出了更高的要求。要求工作流平台能够在上层应用系统被大并发量的用户访问时,低层的工作流平台能够并发的、准确的、高效的响应应用系统对工作流平台的对象调用。也就是说,应用系统的压力最终转化为了对工作流平台对象调用的压力。因此,工作流处理对象调用时的性能就成为了工作流产品性能中最重要的决定因素。

本文在西安协同软件产业(集团)股份有限公司的工作流管理系统SynchroFLOW®产品项目中对工作流引擎处理对象调用的模式问题进行了研究和探讨,提出了基于对象池的调用模式解决并发对象调用问题,并研究了对象池的共享数据的处理策略,测试了使用对象池前后的性能差别。本文的成果在SynchroFLOW®产品中实现,并在电信BSS业务系统中成功应用。

1工作流对象的调用模式

工作流产品一般遵循WfMC组织的规范,该规范定义了工作流参考模型,如下图所示,该模型以工作流引擎为中心,外围通过5类接口与工作流引擎交互。5类接口分别是:接口1,工作流过程定义与交换接口;接口2,工作流服务和客户应用之间的接口;接口3,工作流引擎和直接服务之间的接口;接口4,工作流管理系统之间的互操作接口;(5) 接口5,工作流服务和工作流管理工具之间的接口。[1]

图1 工作流接口调用图

其中应用系统与工作流引擎之间通过WAPI调用的方式进行交互,WAPI调用在工作流引擎内部转化为对工作流实例对象的调用,工作流实体对象包括过程实例对象、活动实例对象、工作项实例对象、相关数据实例对象等。在工作流引擎承受较高并发访问的情况下,实例对象会同时被多个线程调用。为了避免对象行为冲突,必须对这些实例对象加锁,当一个线程调用完该对象后,另外一个线程才能再次调用该对象。

假设我们用a表示工作流的一个实例对象,比如过程实例对象,b表示对象a的一个方法,则对象a可表示为一个向量:

a =(b1,b2,•••,bm)

由于对象a的内部逻辑是固定的,也就是bm是有顺序的,必须是b1先被执行,然后是b2才能被执行。

假设有多个线程t1,t2,•••调用对象a,由于各个线程的执行速度不同及其多线程的随机性本色,执行可能会变为:

t1:b1,b2,b3•••,

t2:b3,b4,•••,

t3:b1,b3,•••,

•••

上图中的t2和t3都出现了行为交叉现象,这是由多线程程序的本质决定的。为了避免这种情况,只有对a加锁,也就是使t1、t2、t3•••按照顺序串行:

t1(b1,b2,b3•••) →t2(b1,b2,b3•••)→t3(b1,b2,b3•••)

这样势必导致对象调用串行,而串行的对象调用的效率是比较低的。比如工作流过程实例对象的创建过程实例操作是一个耗时操作,需要频繁读写数据库,需要1秒种,那么2000个线程完成创建过程实例就需要1x2000=2000秒。

2 对象池模式的设计及实现

为了解决工作流对象串行低效的问题,本文采用了对象池的调用模式。在对象池模式下,多个线程可以同时调用工作流实例对象,而且相互之间不会有交叉、冲突的现象。因此对象池是实现对象池模式的核心内容,它通过缓存工作流实例对象来实现对象的并发调用[2]。

如果实例对象用到了外部数据,那这个外部数据就称为共享数据。对象池内的对象被并发调用时,共享数据就必须进行同步加锁。

2.1 对象池原理

对象池就是存放多个相同对象实例的数据结构,它把被频繁调用的对象构造好存放在对象池内。如果某个线程要调用该对象,首先要从对象池里“借出”,然后使用,执行对象的方法,或者为对象设置属性。用完后,再把该对象“还回”对象池。还回之前还要对对象进行“校验”,看该对象是否符合重新进入对象池的标准,如果不符合标准,则被“废弃”,如果符合标准,则重新进入对象池,下次该对象还可以被再次借出。如果向对象池借出对象时对象池内的对象已经被全部借出,那么就会进行等待,直到有某个对象被还回。

如果用δ表示工作流过程实例对象的对象池,则δ可以表示为:

δ={ a1,a2,•••,an }

其中an表示被缓存的工作流过程实例对象,an在对象池是没有顺序的,δ是一系列an的集合。

假设有多个线程t1,t2,•••调用过程实例对象a。在对象池模式下,t1从δ借出一个对象ai,t2从δ借出一个对象aj,••• i,j∈[1,n],那么多个线程的执行结果就是:

t1:b1,b2,b3•••,

t2:b1,b2,b3•••,

t3:b1,b2,b3•••,

•••

由于多个线程他们调用的是过程实例对象a的不同实例,该对象实例有其自己的对象空间。在自己的对象空间里,对象可以自由的对属性进行设置、或者调用方法,不会对别的对象造成影响,因此,多个线程同时运行,而且相互之间不会交叉、冲突。

在对象池的模式下,如果有2000个线程要执行工作流过程实例对象的创建过程实例操作,那么它们耗费的总时间计算如下:

每个对象在串行方式下的调用时间是1秒钟,由于在对象池模式下多个对象并发的在执行,必然会使单个对象的平均执行时间变慢,假设平均单个对象的调用时间增大为10秒钟,再加上从对象池借出和还回对象的时间,不会超过10.1秒钟;

假设工作流预先设置的对象池大小为100,那么总时间为:10.1 x (2000 / 100) = 202秒,对象池模式下,对象调用效率提高了89.9%。

对象池的使用,避免了多个线程访问同一个对象造成的对象行为冲突,而且使得多个线程可以同步运行。由于对象不会被频繁构造,大大节省了系统构造对象的资源,对系统效率有很大的提升。

2.2 对象池的实现

为每个工作流实例对象创建一个对象池。对象池在工作流管理系统启动时进行初始化,设置对象的各种参数,包括对象池大小、是否等待、是否自动创建对象等。

比如创建活动实例对象的对象池,类定义为:

class PoolableActivityControlFactory

implements PoolableObjectFactory{

private int maxActive;//最大对象数

private boolean exhausted;//是否等待

private long maxWait;//最长等待时间

private Boolean testOnBoorow;//借出时是否进行有效性检查

构造方法:

public PoolableActivityControlFactory() {

}

创建对象:

public Object makeObject() throws Exception{

return new ActivityControl();

}

判断对象是否有效:

public boolean validateObject(Object object){

if(((ActivityControl)object).activityImpl==null)

return true;

else

return false;

}

销毁失效的对象:

public void destroyObject(Object object) throws Exception{

}

激活对象

public void activateObject(Object object) throws Exception{

}

挂起对象:

public void passivateObject(Object object) throws Exception{

}

}

由于预先把对象缓存到对象池中势必要占用内存资源,因此,要根据机器资源情况、预估系统压力情况,恰当的设置对象池大小。

2.3 共享数据的同步

对于对象池δ内的对象an,如果引用了另外一个静态对象p,也就是说对象p就好像是对象池内的全局变量,我们把这种对象p称为对象池的共享数据。

δ=(a1(p), a2(p),•••,an(p))

在多个线程t都访问对象池时,势必出现有些线程同时操作对象p的情况。为了避免对象p的行为被交叉,必须对p进行加锁。[3]

举例如下:

class A{

public static P p=new P();

void method1(){

synchronized(p){… …} //对对象p加锁

}

}

class P{

}

需要注意的是,由于对p进行了加锁同步,那么多个线程t访问对象池时,由于各个对象都访问共享数据p,那么必然出现多个线程在p这个地方排队等待的现象。那么这个是不是就违背了整个对象池并发访问的初衷了吗?其实这个问题的解决方案就是共享数据p被加锁的部分的代码一定要是“轻量的”、运行速度快的代码,比如让p执行一个“n++”操作,执行list.add()操作等此类的操作,使得线程能够在加锁的地方快速执行,从而不会对整个对象池的并发性能造成影响。

3 性能测试分析

在实际运行当中,在采用对象池模式的同时还要考虑到机器性能、数据库性能、工作流模型大小等各种因素对系统性能的影响,因此还没有一个固定的规则用来对对象池大小等参数进行配置调整。这种情况下,只能借助于专业的系统性能测试工具对工作流系统进行测试,以对比在对象池模式使用前后系统性能的变化情况。

本文采用美国Mercury公司的压力测试工具LoadRunner对工作流系统进行压力测试[4]。测试方案是通过LoadRunner脚本调用协同工作流管理系统SynchroFLOW引擎的API对象,在固定的2分钟内模拟并发5个用户、10个用户、20个用户。主要统计分析指标是对象调用的平均相应时间、总的事务数及CPU的使用率。

下图中的场景1是未使用对象池模式的测试场景,场景2是使用对象池模式的测试场景。

图2 平均响应时间图

图3 完成的请求数

从图中的数据可以看出在5、10、20个用户时,采用对象池后工作流对象调用的平均响应分别减少了了77.5%、67.3%、64%。性能提升明显。

随着场景2性能的提升,在2分钟内场景2完成的请求数也大幅度提升。

观察图4中CPU使用情况,效果最明显。在场景1的测试条件下,由于工作流对象的响应是串行的,因此CPU占用率一直保持在50%左右。而在场景2情况下,由于并发调用对象,CPU的使用率接近100%。对象池模式下能够有效的利用机器资源。

图4 CPU使用率

从上面的测试可以看出,对象池模式可以明显提升工作流对象调用的响应性能,而且可以通过配置对象池的大小,充分发挥机器效能,获得更好的整体性能。

4 结束语

目前基于工作流的信息系统对工作流平台的要求越来越高,而稳定高效的对象调用机制是构造高效工作流产品的重要基础。通过SynchroFLOW®产品的实践证明,基于对象池模式的对象调用机制能够明显的提高产品性能,提高服务器资源利用率。

下一步将进一步研究如何通过对系统性能的监控来自动调整对象池的大小,使得对象池能够智能的适应不同机器系统资源的变化,适应并发量的变化。这样将更能促进工作流系统对资源的有效利用。

参考文献:

[1] 范玉顺 工作流管理技术基础 清华大学出版社 2001

[2] 水超,李慧 对象池模式的扩展与设计 计算机工程 2004,09

[3] 沈勤祖,郑仕辉,郭亮 Java虚拟机多线程之研究 云南大学学报(自然科学版) 1997,06

[4] 张俊英,张平,张凌 J2EE平台下的大型MIS系统的负载测试方法研究 中国测试技术 2004,04

作者简介:

马灿东(1973-),男,山东滕州人,硕士研究生,主要研究方向为流程集成、服务集成、面向服务架构中间件。