首页 > 代码库 > 性能测试从0到1的过程(二)
性能测试从0到1的过程(二)
本人,从毕业开始接触测试,但是性能方面一无所知。之前在第一份工作,测过安卓客户端,当时写过一个非常简单的shell脚本,push到手机系统内,用于手机硬件信息。但是在服务端的性能方面,真的是一只菜鸟。现在 本鸟把自己最近一个月的性能测试经验分享一下吧。
一、环境准备,这个熟门熟路。之前的帖子已经写了,装zk/kafka这些中间件需要,再是部署应用服务。重点是部署的过程中,首先你要准备好profile要与测试、生产环境都隔离,再者,用于性能测试的环境,最好是模拟线上去配置,机器最好是申请高级一点,配置参数参考线上,哪些参数呢,比如分配的内存、线程数等。具体ps一下看看
-Djava.awt.headless=true -server -Xms2048m -Xmx2048m -XX:MaxPermSize=256m
上面的那一段 具体是分析的内存大小,这些是可以在jvm配置参数上写的。还有一些参数,比如gc日志文件,有些时候打出来是这幅样子
46224.076: [GC (Allocation Failure) [PSYoungGen: 697520K->688K(697856K)] 797758K->100934K(2096128K), 0.0067511 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
很明显格式不规范,时间戳有问题,我们需要转换一下,那么需要配置参数为
-Xloggc:/home/ewallet/loan-test/elephant-xn/tomcat-loan-risk-auto-Ins1/logs/gc.log -XX:+PrintGCDateStamps
这一串意思就是把gc日志打印方式改成datastamps的形式展示出来,这样我们就能很好的查看gc日志了。环境准备大概是这么些,一般压测也会放在执行机上去发起,也就是说我们本地的配置太弱,会影响并发的效率,所以最好拿一台机器作为执行机,配置和应用服务器差不多就行了。对了,这里提一点,执行机上最好看一下jdk版本,我们的测试脚本都是1.8基础上写的,如果执行机jdk太低,注意要提前准备好~
二、测试脚本编写
我这边要测的一个是dubbo接口,一个是消息监听类,而我们性能测试脚本一般是python写的,用grinder去支持。无奈,python无法编写dubbo接口测试,所以咯,这里的思路是先编写dubbo接口测试类,再打成一个jar包,放在python工程下,此时再去写代码调用里面的测试接口,就能成功发起请求啦。说来容易,首先我们的dubbo接口工程真的太庞大,打包后真的非常非常臃肿,所以我又重新开始写测试工程,只写用到的接口。我们这边用到的测试工程是spring+testng,写起来倒是挺快的,一共是3个类。写完调通后,开始打包。这里我又遇到问题了,之前我的打包方式都是代码和依赖分开打包,至少在jmeter测试的时候是分开的,但是这里要合并!就是要合成一个完整的jar包!哎,应该是我这个idea版本不支持,这方面折腾了很久,下面列出pom里打包用到的pluguin,因为我后面用了eclipse进行打包测试。
1、用jemer测试性能的话,需要代码和依赖分开打包,用到的build配置为。这样子在target下会有两个jar包生成。
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <id>copy</id> <phase>package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory>${project.build.directory}/lib</outputDirectory> </configuration> </execution> </executions> </plugin> </plugins> </build> <dependency> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>3.0.2</version> </dependency>
2、用grinder测试的话 需要把整个java测试工程包含依赖全部打包在同一个包内。依赖为
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>2.4.2</version> <configuration> <skipTests>true</skipTests> </configuration> </plugin> <!-- <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <id>copy</id> <phase>package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory>${project.build.directory}/lib</outputDirectory> </configuration> </execution> </executions> </plugin> --> <plugin> <artifactId>maven-assembly-plugin</artifactId> <configuration> <span style="color:#FF0000;"> <archive> <manifest> <mainClass>com.allen.capturewebdata.Main</mainClass> </manifest> </archive> </span> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> </plugin> </plugins>
后来遇到了一个报错,那么需要再新增一个插件配置
<plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.7</source> <target>1.7</target> </configuration> </plugin> </plugins>
这样子成功打出了两个包 分别为maventest-0.0.1-SNAPSHOT.jar 和 maventest-0.0.1-SNAPSHOT-jar-with-dependencies.jar 当然我需要的是后者。
3、好了,java代码搞定,那么需要一个python测试工程了,Grinder3的脚本引擎是Jython,它是Python在Java中的完全实现。Python学习参考 script gallery and Richard Perks‘ tutorial。环境配置方面,需要准备grinder和jython,还有一个pvdev的配置,用到的开发工具是eclipse。然后新建一个测试工程,类型选择pvdev project ,其他的,就是编写python脚本,再把jar包的依赖放入lib文件,在properties里增加依赖。这里列出一个http接口测试类的编写,主要实现过程是参数从外部文件中读取,发起请求是通过httpclient去实现。
# -*- encoding:utf-8 -*- from net.grinder.script import Test from net.grinder.script.Grinder import grinder from java.util import HashMap import random import time from java.net import URLEncoder from net.sf.json import JSONObject from java.util import UUID url = "http://xxxxx/rest/" logfile = open("err.log", "w") isopen = AtomicInteger(int(grinder.getProperties().get(‘grinder.threads‘))) ISOTIMEFORMAT = ‘%Y-%m-%d %X‘ #random = random() #全局变量,获取grinder.properties中定义的进程数和线程数 process_num = int(grinder.getProperties().get(‘grinder.processes‘)) thread_num = int(grinder.getProperties().get(‘grinder.threads‘)) infile = open("out.txt",‘r‘) bank_account_ids=[] quick_pay_ids = [] m_user_ids=[] identity_nos = [] for line in infile.readlines(): if(line!=None): tmp = line.strip().split(‘,‘) bank_account_ids.append(tmp[0]) quick_pay_ids.append(tmp[1]) m_user_ids.append(tmp[2]) identity_nos.append(tmp[3]) infile.close() nums = len(quick_pay_ids) print "nums:", nums class TestRunner: def getParam(self,bank_account_id,quick_pay_id,m_user_id,identity_no): # requestId每次需要变 #request_id = "12345678-1234-1234-1234-123456789101" uuid = UUID.randomUUID(); request_id=uuid.toString().upper() insure_serial=random.randint(1000000000, 90000000000) paramsMap = HashMap() paramsMap.put("version","1.0") paramsMap.put("request_id",request_id) data = JSONObject() data.put("product","pc_test") data.put("m_user",m_user_id) print "ready data:",data paramsMap.put("data", data.toString()) print "ready params:",paramsMap return paramsMap def DoTest(self,param): result = HttpUtils.httpPost(url,param) print "result", result if(result.getContent() == None): print "response is null" grinder.getStatistics().getForCurrentTest().setSuccess(False) logfile.write(time.strftime(ISOTIMEFORMAT, time.localtime( time.time())) + ‘\n‘) if(result.getCode() != 200 ): print "response status !=200" grinder.getStatistics().getForCurrentTest().setSuccess(False) logfile.write(time.strftime(ISOTIMEFORMAT, time.localtime( time.time())) + ‘\n‘) request = Test(1, ‘DoTest‘).wrap(DoTest) def initialSleep( self): sleepTime = grinder.threadNumber * 100 # per thread grinder.sleep(sleepTime, 0) def __call__(self): """This method is called for every run performed by the worker thread.""" if grinder.runNumber == 0: self.initialSleep() # 按顺序取数据 id = (process_num * thread_num * grinder.getRunNumber() + thread_num * grinder.getProcessNumber() + grinder.getThreadNumber()) % nums param = self.getParam(bank_account_ids[id],quick_pay_ids[id],m_user_ids[id],identity_nos[id]) self.request(param) def __del__(self): if isopen.decrementAndGet() == 0: logfile.close()
注意
-
脚本必须定义一个名为TestRunner的类
-
TestRunner实例必须是可调用的
- 测试脚本可通过grinder对象访问
我这边还编写了其他如dubbo接口、消息监听接口测试类,这里暂时不一一列出了。
三、到了激动人心的时刻,开始测试。注意,测试脚本最好在本地调通,然后上传到平台去执行。这里涉及测试数据的准备,一般人是会用shell或者其他去写,最好建议自动化掉。执行的过程我是关注以下几个指标。包含基础模块,也就是
- 系统load:先看load15 按cpu核数来对比,对于1核的机器,【0.7,1)的状态是比较好的,达到1的话就是即将拥堵,需要排查,上限一般是根据机器核数来定
- cpu占比
- 磁盘util
- javamethod统计
- sql使用
- jvm
差不多就上面这些指标。
性能测试从0到1的过程(二)