且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Aperlib——Socket通讯模块压力及大数据对比工具

更新时间:2022-09-22 15:37:32

1 背景
在我们的应用中的程序有很多是socket通信模块,对于socket通信相关的模块的测试存在如下的问题:
1. 驱动模块开发工作量大,形式各异,不宜维护
随着自动化测试的开展,我们在进行功能测试时,普遍使用测试子系统的模式,对于socket通讯模块,我们普遍采取的是用驱动发送请求,接收结果的方式,针对每一个模块的驱动,我们都要写关于请求发送,连接管理,接受请求的代码,这些代码其实都差不多,这就有了很多的重复的工作量。而这些重复的也是可以避免的。虽然这些重复的代码实现的功能基本相同,但是对于不同的开发者而言,这些代码会写成不同的样子,这些驱动的使用方法也可能有很大的不同。这就给维护和使用造成了一些额外的学习成本。
2. 性能测试环境复杂,工具开发困难
很久以来,我们的性能测试和压力测试都采用的向最上端的webserver发送压力,然后通过一系列中间模块将压力请求发送到后端被测模块,采用这样的模式会有如下问题:
(1) 进行后端模块的性能测试需要搭建一串的相当复杂的环境,占用相当多的机器。而且模块多,搭建环境的成本也会比较高。
(2) 由于压力请求经历的模块很多,各个模块都有可能在配置或者环境上出现问题,当发生问题是定位原因也就是一个很复杂的过程。
(3) 由于中间经历了多个模块的转发,对于下游模块的某些输入会在上游模块过滤掉。我们在压力测试中会忽略掉一些会影响性能的重要因素,导致性能测试不准确,可能会遗漏掉一些性能问题
因而,我们针对每个模块的压力工具,通过这个压力工具进行压力和性能测试,会是一种较好的解决方案。但是对于每个模块都写一个压力工具的开发量是比较大的。
3. 大数据对比效率低,定位问题困难
大数据对比是一种很好的保证程序回归功能点的方法,现阶段大数据对比的方法在我们测试中用的也越来越多了,在使用过程中出现了一些问题:现阶段的socket通讯相关模块的大数据对比是通过用驱动分别向新老版本发送一条请求,然后将接收到的结果打印到文件中,通过对比两个输出文件的数据来判断结果数否相同。这种做法有如下一些弊端:现在的驱动都不支持压力功能,发送完一个请求驱动就退出,这样的话,大数据对比的效率很低速度很慢;输出的结果文件的大小可能很大,这就对进行结果diff造成了一些困难,定位问题也比较麻烦。
2 Aperlib的解决方案
通过对上面问题的分析,我们提供了aperlib来解决上述问题,提供驱动和压力工具框架lib,进行快速的driver和压力工具开发,解决了驱动程序开发的效率及维护和压力工具开发和性能环境复杂问题,提供大数据对比工具开发的框架库进行大数据对比工具的开发,解决了基于socket通信模块的大数据对比的效率低下和定位问题困难的问题。具体的解决方案说明如下:
1. 快速压力工具开发框架,降低压力测试环境复杂度
通过我们针对以上的问题进行了分析:我们的不同程序的压力工具,不同之处在与和下游被测模块的接口,包括发送的请求、接收结果的接口和从词表文件中获取请求数据,其他的对于连接的管理,压力发送数据的管理,压力大小的控制,压力结果数据的管理都是一致的,而且压力工具的功能和逻辑是相同的,只是一些功能的具体的实现方式有所差异,因而,我们可以将程序的基本框架和相同的功能在一个类中实现,而将可能有差别的地方用虚函数接口暴露在程序外面,通过针对不同的被测程序模块的特点,继承这个类的子类来,重写这些函数来实现各个模块的压力和大数据对比工具。下图黑色部分为压力工具中的相同的不需重写的功能,红色为实现不相同的功能,对应的蓝色为对应的aperlib的函数接口。
 

Aperlib——Socket通讯模块压力及大数据对比工具



图1. Aperlib压力的功能及重用接口说明图示
我们有了测试压力工具,那么我们的性能和压力测试就不需要搭建被测模块上游的一系列的模块,而且对于输入的信息我们可以不怕上游模块的过滤***构造了,以下图为例说明一下性能环境的精简。
 

Aperlib——Socket通讯模块压力及大数据对比工具 Aperlib——Socket通讯模块压力及大数据对比工具




图2 性能测试环境精简图示
2. 驱动和压力工具共用,降低开发成本
我们再来分析一下压力工具和功能测试所用的驱动模块的区别:压力工具是发送大量的请求给被测模块,有发送请求接受结果和将结果打印出来的功能,而我们功能测试所用的驱动模块是发送少量的请求,也有发送请求接受结果和将结果打印出来的功能,实际上他们的区别只在于发送请求的数量上了,那么我们将压力工具的压力词表的数据变少,实际上就是一个驱动程序了,也就是说我们程序的驱动和压力工具可以用一个程序来实现。我们在写压力工具的时候我们的驱动程序也就随之产生了
3. 压力工具代码重用,大数据对比工具轻松开发,快速进行大数据对比
Aperlib提供大数据对比工具的开发框架库,和压力工具框架相类似,它提供不同模块的大数据对比工具的共同部分的功能代码,将不同部分用虚函数的形式提供给开发者,通过对虚函数的重写,来实现不同大数据对比工具的不同部分的功能。
 

Aperlib——Socket通讯模块压力及大数据对比工具



图3 Aperlib大数据对比的功能及重用接口说明图示
由上面的图示我们可以看到,大数据对比工具的很多功能部分是和压力工具是一致的,特别是一些需要重写的虚函数,在实质上这些代码是可以重用的,我们通过重用这些代码能够减少很多的工作量,在有压力工具的基础上进行大数据对比工具的开发的速度是非常快的。
Aperlib提供的大数据对比工具实现的是以压力方式发送请求,并在内存中对结果进行对比,将请求的和结果相对应,因而大数据对比的效率很高,而且各种信息提供的很全面,进行问题的定位也很方便,另外由于模块升级时很有可能会有接口上的变化,因而aperlib支持对于不同接口的socket通讯的模块的大数据对比
3 aperlib的程序逻辑及组成
aperlib中有压力和大数据对比两个功能,下面我们分别介绍一下:
1. 压力工具的基本逻辑及组成
压力工具的基本逻辑见下图:
 

Aperlib——Socket通讯模块压力及大数据对比工具



这里有两个pool:data_pool和connection_pool,分别用来管理压力的请求信息和连接。有三个线程来实现数据读取,发送请求,检测连接和接收结果数据的功能。读取数据线程从压力词表中读取请求的文本信息,并将这些信息转化为被测模块所需要的请求格式(如二进制数据结构等),然后将数据放到data_pool中。发送线程分别从data_pool和connection_pool中获取请求数据和可写的连接,将请求数据通过可写连接发送给被测模块,并修改连接状态,放回到connection_pool中,并控制压力大小。check和接收数据线程从connection_pool中获取可读连接句柄,将结果数据读取回来,并根据设定将结果写入到文件或者丢弃,另外通过check连接的状态,将可用连接法如connection_pool,并将超时或者错误连接重建。
针对上面的功能,aperlib提供了如下的类来实现相对应的功能:readdata类实现请求数据管理的功能,cepoll_ctl类实现连接管理相关功能,stress_ctl类实现压力控制功能,stress_template类通过调用上面三个类的接口函数实现三个线程的具体功能。
2. 大数据对比的基本逻辑及组成

Aperlib——Socket通讯模块压力及大数据对比工具



和压力工具相比,大数据对比工具多出来了一个连接池和res_data_pool,多出来的连接池主要是进行对比需要向两个被测模块发请求,需要两个连接池,res_data_pool是用来存储和管理两个被测模块的结果数据,同时,check和接收数据线程增加了数据对比的工作。其他的基本和压力工具相似。
和压力工具类似,aperlib提供了如下的类来实现相对应的功能:readdata类实现请求数据管理的功能,cepoll_ctl类实现连接管理相关功能,stress_ctl类实现压力控制功能,stress_compare类实现了结果数据管理的功能和通过调用上面三个类的接口函数实现三个线程的具体功能。
4 基于aperlib的工具的开发过程
这对我们的用法,aperlib中cepoll_ctl类和stress_ctl类的功能对于所有的程序的压力工具都一致,没有需要更改的必要。在开发过程中需要重写readdata类和stress_template类或者stress_compare类的相应接口,压力工具或者大数据对比工具的开发都有三步,分别如下:
4.1压力工具的开发的三步走:
1 第一步:readdata类的继承和函数重写
通常情况下Readdata类需要重写的函数只有一个:
void * getelement(int &len,char * data_char);//这个函数是将从文件中读取的文本数据(data_char转换成需要发送的void *数据,len为传出的将要发送的数据的格式 )
2 第二步:stress_template类的继承和函数重写
int SendReqToServer(int sock,void * data1,int datalen);//发送请求的函数,这个函数在一般情况下时不用重写的,重写的场景:在databuf中存储的请求本身是变长的,需要再次解析;发送请求的函数经过了封装不是用的标准的send函数
int GetResultFromServer(int sock,FILE * fd);//接受请求的函数,这个函数几乎是必须重写的,因为返回的长度都是可变的。fd是输出的文件的句柄,用来将数据写入到文件中的,如果fd==NULL,表示conf中没有配置将结果写出的配置项
这个类的函数具体实现由于篇幅有限就不在这儿详细说明了,详见《aperlib开发手册》
3 第三步:压力工具的main函数
main函数及main函数调用的几个函数,大部分不需要修改直接使用即可,需要修改的是readdate子类和stress_template子类的类名及下面函数的一个参数
ret= cp_ad_tem_c.init(epoll_c,stress_c,read_c,Conf,sizeof(Query_t));//这里需要特别的注意,这个参数的值为发送请求的数据的长度,如果是变长的请求,这里的值为请求可能的最大值
具体的源码就不在这儿详细说明了,详见《aperlib开发手册》
好了,按照上面的步骤,做完这三步,一个崭新模块的驱动和压力工具的开发就完成了,编译通过后就可以使用了。
4.2 大数据对比工具开发三步走
1 第一步:readdata类的继承和重写
大数据对比压力工具的readdata子类的重写和压力工具的完全相同,他们之间可以复用,这儿就不再重复了
2 第二步:stress_compare类的继承和重写
virtual int SendReqToServer(int sock,void * data1,int datalen);
virtual int SendReqToServer_A(int sock,void * data1,int datalen);
virtual int SendReqToServer_B(int sock,void * data1,int datalen);
virtual int GetResultFromServer(void* res_buf,int sock);
virtual int GetResultFromServer_A(void* res_buf,int sock);
virtual int GetResultFromServer_B(void* res_buf,int sock);
virtual int diff_res(void * res_a,void * res_b);//大数据对比的对比函数,可以根据需要将结果进行对比,相同返回0,不同返回1
virtual int write_send_data(FILE * out_f,int flag,char * send_data);//将发送请求的数据写入到out_f文件中,flag表示是A的还是B的请求数据(新老版本接口不同时需要用到)
virtual int write_res_data(FILE * out_f,int flag,void * data);//将结果数据写入到out_f文件中,flag表示是A的还是B的请求数据(新老版本接口不同时需要用到)
注意:从上面的声明,我们看到有很多的函数需要重写,但是实际上SendReqToServer_A、SendReqToServer_B和GetResultFromServer_A, GetResultFromServer_B的默认实现是调用了SendReqToServer,GetResultFromServer,只有在新老版本对比的接口发生变化时才需要重写的,此时不需要重写SendReqToServer,GetResultFromServer函数,其他情况下(当接口没有发生变化时),不需要重写这些函数,只需要重写SendReqToServer,GetResultFromServer函数就可以了。Send的三个函数的写法基本相类似,GET的三个函数也基本相同,这儿就不详细介绍他们的具体实现了,具体实现方法及说明参数见《aperlib开发手册》
3 第三步 main函数的实现
对于所有的大数据对比工具的main函数也基本相同,需要注意的是一下一句函数调用
ret=pb_compare.init(&read_c,sizeof(req_t),MAX_RESULT_LEN);//这儿要注意后两个参数,他们分别为请求数据和返回数据的大小,当请求和返回数据是变长或者对比的数据的结构体长度不一致的时候,这两个值的大小一定是请求的最大可能值和返回的最大可能值
具体的代码和函数见《aperlib开发手册》
5 aperlib开发的工具的使用方法
由于基于aperlib开发的工具的使用基本类似,关于请求词表的格式和结果文件的格式各不相同,但是使用的配置项和方法是一值的,这儿重点介绍配置项及他们的作用:
5.1 压力测试工具的配置项:
#日志相关
LOG_PATH : ./log/
LOG_NAME : stress.
LOG_LEVEL : 8
LOG_SIZE : 100000

#压力此表的路径和名称,aperlib开发的工具支持多个压力词表,他们使用数字标识的,例如下面的配置,实际上程序会从as_input_data.0开始读取,读完一个文件后将文件编号+1,知道找不到文件为止
INPUT_FILE_PATH : /home/work/presslist/
INPUT_FILE_NAME : input_data
FILE_INIT_NUM: 0

#是否循环发送压力词表,
IS_LOOP_DATA :0
#压力词表的循环次数,当IS_LOOP_DATA为1是生效
LOOP_TIMES: 20000
#发送压力的目的机器的域名或者ip和端口号,host优先于ip
HOST :baidu.com
IP:127.0.0.1
PORT :8911

#epoll的timeout值
TIME_OUT :1000
#压力大小
DEFAULT_STRESS: 300
#控制压力检测时机的值,这个值表示发送多少次请求后采取查看发送所消耗的时间并对压力进行控制,换句话说就是发送这个值词请求所消耗的时间应该是一值的,这个值越大,压力控制的准确度越低,压力越不均匀(不是对于以秒为单位的请求,而是对于更小的时间单位而言),瞬时并发越高,这个值越小,压力的准确度越低,压力越均匀,瞬时并发越低,这个值的配置不能高于DEFAULT_STRESS,而且尽量能整除DEFAULT_STRESS
CHECK_FREQ_NUM : 10
#同时连接的数目,这个是aperlib开发的工具同时向被测模块所开的连接数目,当压力越大时,请将这个配置项酌情调高,线下测试这个值尽量小于1000
SOCK_NUM:100
#是否将结果输出到文件中,以及文件的路径及名称
IS_OUT: 1
OUT_FILE_NAME : ./out
#超时控制所用的参数,单位为微秒
READ_TIMEOUT: 600000
CONNECT_TIMEOUT:100000
WRITE_TIMEOUT:100000
#连接方式,当这个值为1是表示长连接,为0时表示短连接
CON_TYPE :1
#retry次数,当不需要retyr时配置为0
RETRY_TIMES : 10

5.2 大数据对比工具的配置项:
#日志相关
LOG_PATH : ./log/
LOG_LEVEL:8
LOG_NAME:compare.
LOG_SIZE:22222222222222
#输入的文件路径及名称
INPUT_FILE_PATH:./
INPUT_FILE_NAME:./inptudata
FILE_INIT_NUM: 0
#是否需要循环读取数据,及循环次数
IS_LOOP_DATA:0
LOOP_TIMES:100
#epoll的timeout
TIME_OUT:100
#长短连接设置:0为短连接,1为长连接
CON_TYPE:1
#对比服务器设置,其中host优先于ip,当接口不同时,A配置的接口要和程序中send和rec函数的名称中的A相对应,同样B和程序中的B函数相对应
#HOST_A:127.0.0.1
IP_A:127.0.0.1
PORT_A:7001
#HOST_B:7002
IP_B:127.0.0.1
PORT_B:7001
#建立的连接数和压力大小接check压力的频率
SOCK_NUM:200
DEFAULT_STRESS:100
CHECK_FREQ_NUM:5
#是否需要将结果打印到文件中(正确结果的打印),0为不打印1为打印
IS_OUT:0
#输出文件的路径及名称,is_out=1时生效
OUT_FILE_NAME:
#不一致文件输出的路径很名称,这儿需要注意的是,为了防止文件过大,输出文件也是用序号表示的,重0开始,当写入的结果条数等于NUM_PER_FILE时切换到下一个文件,当程序正在使用这个文件时,请不要删除该文件,否则会导致该文件的后续数据丢失
DIFF_FILE_NAME:./diff.txt
NUM_PER_FILE:1000000
6 生成基于aperlib的工具源码框架的脚本
为了方便大家的开发,aperlib提供get_aper_file.sh生成代码框架,他能帮助大家生成各个子类的文件及代码框架,main函数文件和代码和makefile文件框架,具体使用方法如下:
get_aper_file.sh flag project_name send_data_len rec_data_len,其中flag标识的是要生成压力工具(0)还是大数据对比工具(1)的源码框架,projec_name为生成工具的名称,read_data的子类的文件名为${projec_name}_readdata.h,子类名为${projec_name}_readdata,stress_template的子类名为${ projec_name}_template.h,子类名为${ projec_name}_template。send_data_len为存储的发送数据单元的长度,rec_data_len为接收数据单元的长度(当生成压力工具是该参数不需要)
7总结
通过在一些模块中的试用,aperlib的压力功能已经相对比较稳定,实践也证明了用aperlib进行驱动和压力工具的开发成本减少很多。希望aperlib能够帮助大家提高大家的驱动开发和大数据对比工具开发的效率,同时也能对我们的压力和性能测试,大数据对比测试有所帮助。

baiduqaqablog@baidu.com

 

【本文转自百度测试技术空间http://hi.baidu.com/baiduqa/blog/item/e2c00dedd015a603fdfa3c48.html
关注百度技术沙龙















本文转自百度技术51CTO博客,原文链接:http://blog.51cto.com/baidutech/744450,如需转载请自行联系原作者