性能测试

发布于: 2020 年 07 月 21 日
性能测试

以下两题,至少选做一题

  • 性能压测的时候,随着并发压力的增加,系统响应时间和吞吐量如何变化,为什么?

  • 用你熟悉的编程语言写一个 web 性能压测工具,输入参数:URL,请求总次数,并发数。输出参数:平均响应时间,95% 响应时间。用这个测试工具以 10 并发、100 次请求压测 www.baidu.com。

一、性能测试,压力曲线

随着并发压力的增加,系统响应时间和吞吐量的变化,如上图所示。

开始,随着并发数的增加,cpu利用率上升,QPS相应增加,平均响应时间也在增加(变化不大),资源占用率和吞吐量会响应的增加。且平均响应时间是一个指数增加曲线。

当并发数达到一定程度时,资源占用率达到饱和,吞吐量增长放缓甚至停止增长,响应时间进一步延长。

当并发继续增长时,资源饱和,吞吐量下降,响应时间变很长。

这时候,资源达到瓶颈,每秒都会有很多请求需要处理,会造成进程或线程频繁切换,加大了上下文的切换时间,用于处理请求的时间也变少,也就每秒能够处理的请求数变小,响应时间变长,也就造成用户的等待时间变大。

二、java实现压测工具如下:

public class PerformTestTool {
/**
* 一般的性能测试工具按照该算法,但对本地来说不均衡
* 按照顺序取第百分比
* @param sortList
* @param percent
* @return
*/
private static long computeExecTimePercent(List<Long> sortList,double percent){
return sortList.get((int)Math.ceil(percent*sortList.size()));
}
/**
* 根据总量取随机数(百分比)的平均值
* @param sortList
* @param percent
* @return
*/
private static long computeExecTimePercentByrand(List<Long> sortList,double percent){
int totalSize = sortList.size();
int samples = (int)Math.ceil(percent*totalSize);
List<Long> exeTimeList = Lists.newArrayListWithCapacity(samples);
for (int j=0;j<samples;j++){
exeTimeList.add(sortList.get(RandomUtil.randomInt(totalSize)));
}
return (long)exeTimeList.stream().mapToLong((x)->x).average().orElse(0);
}
/**
*
* @param url
* @param reqtotalCount
* @param concuurent
* @return
*/
private static ExecRet exec(String url,int reqtotalCount,int concuurent) {
// 1、资源准备,线程池和http链接池等
BlockingQueue<Runnable> workQueue= new LinkedBlockingQueue<>(reqtotalCount);
ThreadPoolExecutor threadPoolExecutor = new MyPool(concuurent,concuurent,20, TimeUnit.SECONDS,workQueue);
threadPoolExecutor.prestartAllCoreThreads();
ConnectionProfile connectionProfile = ConnectionProfile.builder().maxConnections(10).build();
//connectionProfile.setShortConn(true); 是否为短连接
SimpleHttpClientFacade httpClientFacade = new SimpleHttpClientFacade(connectionProfile);
// 2、task 任务提交
CountDownLatch countDownLatch = new CountDownLatch(reqtotalCount);
for (int i=0;i< reqtotalCount;i++){
Task task = new Task(httpClientFacade,url,countDownLatch);
threadPoolExecutor.submit(task);
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
}
//3、获取执行结果
List<Long> exeTimeList = ((MyPool)threadPoolExecutor).getExecTimeList();
if (exeTimeList.size()!=0){
Collections.sort(exeTimeList);
System.out.println("执行数量"+exeTimeList.size()+":"+exeTimeList);
}
double average = exeTimeList.stream().mapToLong((x)->x).average().orElse(0);
ExecRet exeRet = new ExecRet();
exeRet.setAvgExtime(average);
exeRet.setExecTimePercent(computeExecTimePercentByrand(exeTimeList,0.95));
return exeRet;
}
public static void main(String[] args) throws IOException, URISyntaxException {
ExecRet exeRet = exec("https://www.baidu.com",100,10);
System.out.println("平均响应时间(ms):"+exeRet.getAvgExtime()+",95%响应时间(ms):"+exeRet.getExecTimePercent());
}
}

一些为几个辅助实体类:

线程池,继承ThreadPoolExecutor,重写方法实现 每次运行的执行时间

public class MyPool extends ThreadPoolExecutor {
public MyPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
this.startTimes = new ConcurrentHashMap<>();
}
private ConcurrentHashMap<String, Long> startTimes;
@Getter
private List<Long> execTimeList = new LinkedList<>();
@Override
protected void beforeExecute(Thread t, Runnable r) {
startTimes.put(String.valueOf(r.hashCode()), System.currentTimeMillis());
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
long startDate = startTimes.remove(String.valueOf(r.hashCode()));
long diff = System.currentTimeMillis() - startDate;
execTimeList.add(diff);
}

执行结果类

@Data
public class ExecRet {
private double avgExtime;
private long execTimePercent;
}

任务类,具体执行的任务

@Data
public class Task implements Runnable{
private SimpleHttpClientFacade httpClientFacade;
private String url;
private CountDownLatch countDownLatch;
public Task(SimpleHttpClientFacade httpClientFacade,String url,CountDownLatch countDownLatch){
this.httpClientFacade = httpClientFacade;
this.url = url;
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
try {
httpClientFacade.httpGet(url);
} catch (Exception e) {
System.out.println("========执行失败");
}
countDownLatch.countDown();
}
}

执行结果:

执行数量100:[25, 27, 27, 28, 28, 28, 29, 29, 30, 30, 30, 31, 31, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 36, 36, 36, 36, 36, 36, 36, 37, 37, 37, 37, 38, 38, 39, 39, 39, 40, 40, 41, 41, 41, 42, 42, 42, 43, 43, 44, 44, 44, 45, 45, 45, 45, 45, 46, 48, 49, 50, 50, 51, 52, 52, 54, 54, 55, 57, 57, 61, 62, 63, 65, 68, 76, 80, 201, 506, 506, 507, 507, 508, 509, 511, 513, 514, 517]
平均响应时间(ms):89.21,95%响应时间(ms):80

用户头像

ashuai1106

关注

还未添加个人签名 2017.10.20 加入

还未添加个人简介

评论

发布
暂无评论
性能测试