基于系统真实数据的TUXEDO应用服务器压力测试的研究与实现

发表于:2007-05-31来源:作者:点击数: 标签:测试基于系统真实压力的研究与实现数据
摘要: 在大型的应用项目中,核心业务系统的 压力测试 是一个重要的环节。在系统上线之前,或者在应用的 开发 过程过都是必需的。本文结合一个实际的大型项目,对其压力 测试方案 的设计和具体实施中的一些考虑和遇到的问题进行了分析和研究,提出了一种比较

摘要:

在大型的应用项目中,核心业务系统的压力测试是一个重要的环节。在系统上线之前,或者在应用的开发过程过都是必需的。本文结合一个实际的大型项目,对其压力测试方案的设计和具体实施中的一些考虑和遇到的问题进行了分析和研究,提出了一种比较通用的基于系统真实数据的测试方法,并详细讨论实现方法,进而讨论了相关的要注意的问题。

文章目录:

问题的引出

  1. 模拟出指定数目的客户端
  2. 系统真实数据的获取和传递
  3. 父、子进程的工作及数据通信方式
  4. 测试系统的示意图
  5. TUXEDO参数的调整
  6. 测试数据的生成
  7. 测试的延伸
  8. 要注意的问题及可以改进的地方
  9. 小结

TUXEDO作为一种成熟的事务处理中间件,一般用于大型系统的业务处理。在这样的系统中,一般客户端请求的并发数很大,而且对实时性要求很高,需要在规定的时间内处理完一个事务,并返回结果给用户。而且对系统的稳定性要求也很高,一般都要求7x24运作。为满足以上要求,除了系统各部分有良好的设计、编码和测试外,还需要在上线前对系统的处理能力、极限容量等做一个测试和评估,以便获得关于系统的更真实的性能情况。这种测试是从外部来观察系统的整体情况,比起某个部分的性能评比更有实际的意义,而且使得项目开发人员和以后的运行维护人员对系统的整体性能有一个具体的认识,便于调整和日后的维护。

接下来笔者以自己参与开发的一个大型电信项目为依托,结合自己设计和实现的TUXEDO服务器压力测试工具来分析一下压力测试中的一些方法和过程,以及要注意的问题。

压力测试必须有一定数量的并发客户端。为了测试,准备大量的PC,并在每一个上装一个client程序是不现实的,而且难以做到真正的并发,也不利于客户端数目不断变动的大量的测试,因此我们采用了软件模拟客户端的方法。对TUXEDO服务器而言,每一个客户端就是一个和它通信的进程,所以需要多少客户端简单的说就是开多少调用服务的进程,这个在OS的支持下是很容易实现的。这里有一个问题需要讨论,一般会想到用一个现成的压力测试软件来做。实际中发现,这种方式是有问题的。一般压力测试工具的方法是截获一个client到server的调用数据包,分析其中的数据,然后将一些数据进行参数化,例如一些ID等,然后生成一个可以产生大量并发的同类数据包的脚本,运行该脚本就可以进行压力测试,当然其中包括了很多度量。对于某些应用,例如新开户等,这种方法可以工作得很好,但是对于那些对数据真实性要求很高的服务,这种方法就难以实现。例如笔者做的压力测试中涉及的主要TUXEDO服务——用户帐单的查询和销帐处理。由于用户的电话号码或者帐号等通常是不连续的,中间有很多的空洞,而且销帐的服务是依赖于查询的返回结果的,如果不是数据库中一条真实的可以做销帐处理的帐单是不能成功的完成销帐流程的。这对构造数据带来很大的难度,而且那种构造出来的数据和实际的情况会有很大的差距,因为那可能被数据库的缓冲等进行优化,不能反映真实的性能,而最好的办法当然是用实际数据库中的真实数据。当然,这种真实也可以有一定的扩充,下面会涉及到关于压力测试数据的准备问题。通过上面的讨论,我们知道在一些实际的系统中,让了解系统应用的人自己动手来做压力测试很多时候是一个更好的选择。不必担心这个工作的复杂性,下面我们就相关的问题开始详细的分析。

1.模拟出指定数目的客户端。

开多进程需要OS的支持,下面以UNIX为例给出了一个实现的代码。需要特别注意的是后面注释有*的那一行代码,该行的意义是在子进程(pid=0,fork对父进程和子进程有不同返回,参考[1])中不再执行该循环。在UNIX中,子进程从fork()的下一句开始执行。如果没有上面那一句,新开出的子进程发现满足for循环的条件将继续执行循环,开出新的它自己的子进程,这样会产生复杂的进程树,可以计算那样得出的子进程的数目为。为更好的控制客户端的数量和保持进程相互关系的简明,我们通过上面那一句使得进程的关系只有两层,而且进程数目就等于P_NUM。

for ( i = 0; i < P_NUM; i++ )        //P_NUM为要开的进程数,也就是模拟客户端的数目

{

       if ( (pid = fork()) == 0 ) break;            //*

       if ( pid < 0 ) exit(0);

}

if ( pid == 0 )                       //子进程代码

{

       child_process();

}

if ( pid )                              //父进程代码 

{            

       …

}

通过上面的代码,我们对客户端的产生有了清楚的认识,在进程创建后,父、子进程分道扬镳,开始各自的压力测试之旅。

2.系统真实数据的获取和传递

上面我们讨论了真实数据对压力测试结果可信程度的重要意义,现在的问题是如何设计一个获取数据库中数据并传递给调用服务的客户端的数据流程。这个前提是我们要对被测的服务比较熟悉,了解它的大致处理流程和涉及的数据。

以帐单的销帐为例,它需要销帐方式、操作员工号、涉及的每条帐单的部分信息,这些信息可以由用户的输入和帐单查询服务来得到。用户的输入我们可以在测试程序中设定,帐单的信息我们可以在之前调用查询服务来获取,因为销帐总是在帐单查询之后进行的,所以这个流程也是符合实际情况的。接下来的问题是帐单的查询也是需要有效的输入参数的,我们如何获取这些数据呢?答案自然还是从数据库中来。在真实系统中,帐单查询是前台接收到用户请求,根据用户的电话号码等来查询,在这里我们可以一次从数据库中取出一批有帐单的用户的信息(电话号码等)来作为查询的输入条件。因为这个取编号的操作不是真实系统应有的开销,所以我们可以在模拟客户端开始运行之前完成。

至此,我们对数据的流程有了一个大概的认识。下面就是具体实现的问题了。编号是一次性取出的,而且根据测试需要其数量是可以预先知道的,我们可以将其保存在一个大的数组中,后面我们可以看到用这种数据结构的好处。

获得了最基本的输入,我们就可以开始压力测试程序内部的数据流的设计了。

3.父、子进程的工作及数据通信方式

为保证并发,每个client进程都应该是独立的,而且自身运行必须维持一段时间,这样才能在TUXEDO服务器一端产生大量稳定的连接,进而产生调用,形成压力。每个client进程的工作是得到一个编号,调用帐单查询的服务获取对应用户的帐单,然后整理返回结果,加入操作员信息,再调用销帐服务,获得返回结果。每个client重复上面的过程,重复的次数作为一个参数n,这个是可以配置的,P_NUM * n 就是需要准备的编号的数量,这两个参数需要结合测试的要求来选择,通过改变他们可以进行多次不同的测试。

父进程的工作是获取编号到数组中,然后产生P_NUM个模拟的客户端,接下来它要给这些client分发数据,使得每个client可以持续运行。上面提到的编号数组相当于一个粮食仓库,父进程要给子进程稳定的粮食供应,使其不致于因为“饥饿”而导致停顿。分析这里的数据,还有一个特点就是每个数据都是一次性的,不能再次使用。父进程通过数组的下标可以很容易的保证数据的不重复,这也体现了数组在这种情况下的简洁高效。在父进程输送数据给子进程的过程中,我们考虑后采用了队列来实现,因为要保证每个子进程获得的数据都是唯一的,而且每个子进程最好能从一个已知的地点获取数据,这样便于子进程数目的伸缩,而队列恰好能满足这些要求。由于队列的访问是有控制的,大量的子进程去取可能会造成等待,但是实际测试中我们发现这种影响是很小的,在客户端数目为1000左右时仍不明显,因而我们可以认为队列是高效的。当然这里的问题也可能有更好的解决的办法,队列相对而言比较简单和清晰。

压力测试程序必须返回相应的结果,例如每个调用所耗的时间等等,这里不详细说明,可以根据实际的需要安排相关的代码来实现。

4.测试系统的示意图

通过以上几个方面的讨论,我们对压力测试程序的主体和数据的流程有了认识。以上也是该压力测试方案不同于一般的压力测试工具的地方。下面我们通过一个示意图来给获得一个感性的认识。

测试系统的示意图

5.TUXEDO参数的调整

为配合压力测试,TUXEDO服务器的一些参数需要进行相应的调整。涉及的主要参数是以下几个:

MAXACCESSERS

最大访问者的数目,这个包含多种类客户端,大于P_NUM * 1.2(参考值)。

MAXWSCLIENTS

最大workstation client的数目,要求大于P_NUM。

WSL -m 2 -M 30 -x 10

WSH的数目和每个的并发处理能力。m为WSH的初始数目,M为WSH的最大数目,x为每个WSH可以接受的WS client的连接数。要求M * x > P_NUM。

以上是几个主要的参数,由于连接数要受到license的限制,一般正式license中USERS不会很大,所以进一步的测试可以找一个USERS数目较大的试用license。关于TUXEDO的配置可以参考相应的文档,这里不详细说明。

6.测试数据的生成

由于压力测试是要长时间的运行的,而且要测试很多次,包括不同的配置的情况下,所以真实的数据往往是不够用的,而上面的测试方法要求数据都是在数据库中真实存在的。针对这个问题,我们可以根据真实数据生成一些满足要求的数据。现在一般的DBMS都支持INSERT … SELECT 语句,通过下面的方法可以产生大量的可用数据。

假设某一个表的一条记录以RECORD_ID为主键,max(RECORD_ID) = M;通过下面的SQL语句:INSERT INTO table_name(RECORD_ID, …) SELECT RECORD_ID+M, … FROM table_name; 就可以使表中的记录数double。依此类推,可以使数据量指数级的增长,而这些数据除了一些标识性的ID不同,其他都是以真实数据为蓝本的,比纯粹由测试程序制造出的数据能更好的满足TUXEDO服务程序的要求,实际的测试也证明了这一点。而且通过该方法,结合对业务增长的预测,我们可以模拟系统运行了某一段时间之后的性能情况。

7.测试的延伸

以上主要是针对TUXEDO服务程序的测试,其实以上的测试方法可以用来进行其它方面的测试。这里列举两个主要的方面:

一是TUXEDO的一些负载均衡机制,通过大量clinet的并发操作,可以分析针对具体的硬软件配置情况下,那种配置是最合适的。

二是分析TUXEDO和数据库的连接方式。目前TUXEDO连数据库主要有XA和server直连两种方式。以上方法也可以对这两种方式的实际效果进行评估。

8.要注意的问题及可以改进的地方

为了使压力测试的结果能更接近真实的情况,有一些问题需要注意。

首先压力测试和真实的情况有一个很大的分别是所有的客户端都是在一台机器上,而不是真实环境下的分布在很多的PC上,这样对测试程序所在的机器的性能就有一定的要求。实际测试中我们采用了一台和主机性能接近的UNIX小型机,在模拟客户端数目较大时仍能满足要求。若没有高性能的测试机可用,可以分布在几台机器上,同时运行测试程序。

另外操作系统的一些核心参数也需要考虑并做相应调整,比如一般UNIX系统中队列的容量默认为16KB,不是很大,还有一些IPC的值对TUXEDO性能也有一定的影响,因为TUXEDO使用了Q、SEM、SHM等IPC通信机制,关于这个,TUXEDO官方文档中有详细的讲解。这些可以和系统管理员一起来进行调整。

上面提到的测试程序内部数据流也可以用其它的方式实现。数据库服务器的一些相关参数也可以做相应的调整,使得主要压力集中在TUXEDO服务器,便于观察其在不同负载下的表现,以及在负载非常重的情况下的稳定性和容错性,包括自我恢复的能力。

小结:

笔者根据上面提到的思路和方法为所在的项目进行了长时间的压力测试,并给项目经理和系统管理员提交了正式的压力测试报告,使得大家对系统的一些整体性能有一个比较客观的认识和评估,为满足设计要求并顺利通过验收打下了基础,获得了比较好的效果。该测试程序通过简单的配置和修改又被用于报表等其它的服务模块。

本文是笔者根据上述实践总结归纳出的一些方法和思路,并指出了需要注意的问题,希望能给其他系统的设计和开发人员在面对相关问题的时候提供一些参考。

原文转自:http://www.ltesting.net