写点什么

架构师训练营第七周作业

用户头像
Bruce Xiong
关注
发布于: 2020 年 07 月 22 日

题一

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

因为随着并发压力增加,服务器的网络开销、服务器的负载,以及系统中的还没有处理完成的线程将会阻塞新线程进入,导致新线程等待,所以系统响应时间会升高,自然吞吐量就会下降。



题二

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

主要用到了 CountDownLatch、线程池、httpclient 这些工具类



并发测试需要用到CountDownLatch这个类;CountDownLatch作用是控制N个线程并发执行。

CountDownLatch的原理:是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。

测试结果

以下为100次,10并测试结果,结果顺序是按平均响应时间排序后的。

86:成功数:10,失败数:0,最小响应时间:32,最大响应时间:38,平均响应时间:34.600000
79:成功数:10,失败数:0,最小响应时间:32,最大响应时间:38,平均响应时间:34.800000
71:成功数:10,失败数:0,最小响应时间:33,最大响应时间:38,平均响应时间:35.000000
64:成功数:10,失败数:0,最小响应时间:33,最大响应时间:38,平均响应时间:35.200000
66:成功数:10,失败数:0,最小响应时间:34,最大响应时间:38,平均响应时间:35.200000
69:成功数:10,失败数:0,最小响应时间:33,最大响应时间:39,平均响应时间:35.200000
84:成功数:10,失败数:0,最小响应时间:33,最大响应时间:39,平均响应时间:35.300000
96:成功数:10,失败数:0,最小响应时间:33,最大响应时间:40,平均响应时间:35.400000
51:成功数:10,失败数:0,最小响应时间:33,最大响应时间:40,平均响应时间:35.600000
89:成功数:10,失败数:0,最小响应时间:33,最大响应时间:41,平均响应时间:36.000000
97:成功数:10,失败数:0,最小响应时间:34,最大响应时间:42,平均响应时间:36.100000
23:成功数:10,失败数:0,最小响应时间:33,最大响应时间:42,平均响应时间:36.200000
44:成功数:10,失败数:0,最小响应时间:32,最大响应时间:43,平均响应时间:36.300000
59:成功数:10,失败数:0,最小响应时间:33,最大响应时间:39,平均响应时间:36.300000
81:成功数:10,失败数:0,最小响应时间:33,最大响应时间:39,平均响应时间:36.400000
83:成功数:10,失败数:0,最小响应时间:33,最大响应时间:41,平均响应时间:36.400000
35:成功数:10,失败数:0,最小响应时间:33,最大响应时间:42,平均响应时间:36.500000
21:成功数:10,失败数:0,最小响应时间:33,最大响应时间:42,平均响应时间:36.600000
52:成功数:10,失败数:0,最小响应时间:33,最大响应时间:54,平均响应时间:37.200000
29:成功数:10,失败数:0,最小响应时间:33,最大响应时间:46,平均响应时间:37.300000
36:成功数:10,失败数:0,最小响应时间:34,最大响应时间:43,平均响应时间:37.300000
87:成功数:10,失败数:0,最小响应时间:34,最大响应时间:43,平均响应时间:37.300000
100:成功数:10,失败数:0,最小响应时间:33,最大响应时间:44,平均响应时间:37.400000
53:成功数:10,失败数:0,最小响应时间:34,最大响应时间:43,平均响应时间:37.600000
56:成功数:10,失败数:0,最小响应时间:35,最大响应时间:41,平均响应时间:37.600000
41:成功数:10,失败数:0,最小响应时间:33,最大响应时间:46,平均响应时间:37.700000
61:成功数:10,失败数:0,最小响应时间:33,最大响应时间:43,平均响应时间:37.700000
70:成功数:10,失败数:0,最小响应时间:33,最大响应时间:47,平均响应时间:38.000000
50:成功数:10,失败数:0,最小响应时间:32,最大响应时间:43,平均响应时间:38.100000
99:成功数:10,失败数:0,最小响应时间:34,最大响应时间:44,平均响应时间:38.400000
55:成功数:10,失败数:0,最小响应时间:33,最大响应时间:45,平均响应时间:38.600000
26:成功数:10,失败数:0,最小响应时间:33,最大响应时间:45,平均响应时间:38.900000
31:成功数:10,失败数:0,最小响应时间:32,最大响应时间:45,平均响应时间:38.900000
20:成功数:10,失败数:0,最小响应时间:33,最大响应时间:49,平均响应时间:39.100000
1:成功数:10,失败数:0,最小响应时间:37,最大响应时间:44,平均响应时间:39.500000
58:成功数:10,失败数:0,最小响应时间:34,最大响应时间:48,平均响应时间:39.600000
25:成功数:10,失败数:0,最小响应时间:34,最大响应时间:45,平均响应时间:39.700000
3:成功数:10,失败数:0,最小响应时间:36,最大响应时间:46,平均响应时间:39.900000
10:成功数:10,失败数:0,最小响应时间:38,最大响应时间:44,平均响应时间:40.000000
9:成功数:10,失败数:0,最小响应时间:38,最大响应时间:42,平均响应时间:40.100000
57:成功数:10,失败数:0,最小响应时间:36,最大响应时间:53,平均响应时间:40.400000
90:成功数:10,失败数:0,最小响应时间:34,最大响应时间:51,平均响应时间:40.500000
24:成功数:10,失败数:0,最小响应时间:34,最大响应时间:59,平均响应时间:40.700000
65:成功数:10,失败数:0,最小响应时间:33,最大响应时间:48,平均响应时间:40.700000
16:成功数:10,失败数:0,最小响应时间:38,最大响应时间:44,平均响应时间:40.800000
34:成功数:10,失败数:0,最小响应时间:33,最大响应时间:50,平均响应时间:40.900000
72:成功数:10,失败数:0,最小响应时间:32,最大响应时间:49,平均响应时间:40.900000
12:成功数:10,失败数:0,最小响应时间:38,最大响应时间:45,平均响应时间:41.000000
28:成功数:10,失败数:0,最小响应时间:33,最大响应时间:57,平均响应时间:41.000000
48:成功数:10,失败数:0,最小响应时间:33,最大响应时间:52,平均响应时间:41.100000
63:成功数:10,失败数:0,最小响应时间:33,最大响应时间:67,平均响应时间:41.100000
94:成功数:10,失败数:0,最小响应时间:35,最大响应时间:48,平均响应时间:41.100000
39:成功数:10,失败数:0,最小响应时间:34,最大响应时间:59,平均响应时间:41.400000
92:成功数:10,失败数:0,最小响应时间:34,最大响应时间:47,平均响应时间:41.400000
43:成功数:10,失败数:0,最小响应时间:34,最大响应时间:55,平均响应时间:41.500000
85:成功数:10,失败数:0,最小响应时间:34,最大响应时间:59,平均响应时间:41.600000
32:成功数:10,失败数:0,最小响应时间:34,最大响应时间:58,平均响应时间:41.800000
54:成功数:10,失败数:0,最小响应时间:36,最大响应时间:54,平均响应时间:42.100000
77:成功数:10,失败数:0,最小响应时间:33,最大响应时间:110,平均响应时间:42.200000
30:成功数:10,失败数:0,最小响应时间:34,最大响应时间:54,平均响应时间:42.300000
73:成功数:10,失败数:0,最小响应时间:34,最大响应时间:89,平均响应时间:42.500000
88:成功数:10,失败数:0,最小响应时间:38,最大响应时间:46,平均响应时间:42.600000
38:成功数:10,失败数:0,最小响应时间:35,最大响应时间:55,平均响应时间:42.800000
46:成功数:10,失败数:0,最小响应时间:33,最大响应时间:58,平均响应时间:43.100000
60:成功数:10,失败数:0,最小响应时间:33,最大响应时间:59,平均响应时间:43.100000
67:成功数:10,失败数:0,最小响应时间:33,最大响应时间:65,平均响应时间:43.300000
91:成功数:10,失败数:0,最小响应时间:36,最大响应时间:52,平均响应时间:43.400000
27:成功数:10,失败数:0,最小响应时间:34,最大响应时间:58,平均响应时间:43.600000
76:成功数:10,失败数:0,最小响应时间:34,最大响应时间:53,平均响应时间:43.600000
45:成功数:10,失败数:0,最小响应时间:37,最大响应时间:66,平均响应时间:43.700000
15:成功数:10,失败数:0,最小响应时间:37,最大响应时间:58,平均响应时间:44.300000
93:成功数:10,失败数:0,最小响应时间:33,最大响应时间:62,平均响应时间:44.600000
98:成功数:10,失败数:0,最小响应时间:35,最大响应时间:62,平均响应时间:45.300000
40:成功数:10,失败数:0,最小响应时间:34,最大响应时间:59,平均响应时间:45.700000
80:成功数:10,失败数:0,最小响应时间:34,最大响应时间:62,平均响应时间:45.900000
62:成功数:10,失败数:0,最小响应时间:34,最大响应时间:118,平均响应时间:46.900000
68:成功数:10,失败数:0,最小响应时间:33,最大响应时间:139,平均响应时间:47.300000
47:成功数:10,失败数:0,最小响应时间:34,最大响应时间:64,平均响应时间:48.200000
2:成功数:10,失败数:0,最小响应时间:38,最大响应时间:64,平均响应时间:48.500000
7:成功数:10,失败数:0,最小响应时间:42,最大响应时间:55,平均响应时间:49.300000
74:成功数:10,失败数:0,最小响应时间:34,最大响应时间:133,平均响应时间:49.300000
95:成功数:10,失败数:0,最小响应时间:36,最大响应时间:66,平均响应时间:49.400000
11:成功数:10,失败数:0,最小响应时间:38,最大响应时间:58,平均响应时间:49.500000
82:成功数:10,失败数:0,最小响应时间:36,最大响应时间:63,平均响应时间:49.800000
13:成功数:10,失败数:0,最小响应时间:37,最大响应时间:108,平均响应时间:51.600000
17:成功数:10,失败数:0,最小响应时间:41,最大响应时间:66,平均响应时间:51.900000
5:成功数:10,失败数:0,最小响应时间:44,最大响应时间:63,平均响应时间:52.200000
37:成功数:10,失败数:0,最小响应时间:35,最大响应时间:73,平均响应时间:52.400000
22:成功数:10,失败数:0,最小响应时间:33,最大响应时间:90,平均响应时间:53.100000
6:成功数:10,失败数:0,最小响应时间:40,最大响应时间:71,平均响应时间:53.900000
49:成功数:10,失败数:0,最小响应时间:34,最大响应时间:215,平均响应时间:55.800000
75:成功数:10,失败数:0,最小响应时间:33,最大响应时间:232,平均响应时间:56.100000
19:成功数:10,失败数:0,最小响应时间:35,最大响应时间:168,平均响应时间:56.300000
33:成功数:10,失败数:0,最小响应时间:40,最大响应时间:70,平均响应时间:59.900000
18:成功数:10,失败数:0,最小响应时间:41,最大响应时间:237,平均响应时间:65.000000
14:成功数:10,失败数:0,最小响应时间:37,最大响应时间:183,平均响应时间:69.500000
4:成功数:10,失败数:0,最小响应时间:37,最大响应时间:272,平均响应时间:72.000000
8:成功数:10,失败数:0,最小响应时间:40,最大响应时间:198,平均响应时间:73.300000
78:成功数:10,失败数:0,最小响应时间:35,最大响应时间:275,平均响应时间:74.500000
42:成功数:10,失败数:0,最小响应时间:38,最大响应时间:230,平均响应时间:84.700000
95%平均响应:69.5

核心的代码

负责压力测试

package com.architect.stud;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class Pressure {
private int threads = 1;
private int frequency =1;
CountDownLatch countDownLatch = null;
ExecutorService executor = null;
TestTask test = null;
private int index = 0;
List<TestReport> result = new ArrayList<TestReport>();
List<Future<TaskResult>> futures = null;
private Pressure(int threads,int frequency,TestTask test) {
this.test = test;
this.threads = threads;
this.executor = getExecutor();
this.frequency = frequency;
}
public ExecutorService getExecutor() {
int core = Runtime.getRuntime().availableProcessors();
return new ThreadPoolExecutor(core,
core * 2 + 1,
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(1024),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy());
}
public static Pressure build(int threads,int frequency,TestTask test) {
return new Pressure (threads, frequency,test);
}
private void begin() {
//构造并发执行任务
this.countDownLatch = new CountDownLatch(threads);
this.futures = new ArrayList<Future<TaskResult>>();
IntStream.range(0, threads).forEach(i -> {
Future<TaskResult> future = executor.submit(new PressureJob(countDownLatch,i,test));
futures.add(future);
});
}
public Pressure Test() {
//执行压力测试
IntStream.range(0, frequency).forEach(i -> {
start();
});
return this;
}
private void start() {
begin();
index++;
IntStream.range(0, threads).forEach(j -> countDownLatch.countDown());
//处理测试结果
end();
}
public List<TestReport> getResult(){
this.result.sort(new ResultSort());
return this.result;
}
private void end() {
List<TaskResult> list = futures.stream().map(item -> {
try {
return item.get();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}).collect(Collectors.toList());
list = list.stream().filter(item -> null != item).sorted(Comparator.comparingLong(TaskResult::getResponseTime)).collect(Collectors.toList());
//计算平均值
double average = list.stream().mapToDouble(TaskResult::getResponseTime).average().getAsDouble();
int success = list.parallelStream().filter(item -> item.isOK()).map(item -> 1).reduce(0,Integer::sum);
long maxTime = list.get(list.size()-1).getResponseTime();
long minTime = list.get(0).getResponseTime();
int fail = list.size() - success;
TestReport res = new TestReport();
res.setAverageTime(average).setSuccess(success).setFail(fail).setCurFrequency(index).setMaxTime(maxTime).setMinTime(minTime);
result.add(res);
}
}

负责当单个测试线程执行

public class PressureJob implements Callable<TaskResult>{
CountDownLatch countDownLatch;
TestTask test;
private final int taskId;
public PressureJob(CountDownLatch countDownLatch,int taskId, TestTask test) {
this.countDownLatch = countDownLatch;
this.taskId = taskId;
this.test = test;
}
@Override
public TaskResult call() throws Exception {
countDownLatch.await();
Long start = System.currentTimeMillis();
boolean isOk = true;
if(null != test)
isOk = test.runTest();
Long end = System.currentTimeMillis();
return isOk ? TaskResult.success(end - start) : TaskResult.fail(end - start);
}
}

定义测试任务

public interface TestTask {
/**
* 执行测试
* 值为true表示测试成功
* @return
*/
public boolean runTest();
}

实现测试任务(请求百度)

package com.architect.stud;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import org.apache.hc.client5.http.fluent.Request;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.util.Timeout;
public class TestBaidu implements TestTask{
Timeout timeout=Timeout.of(2, TimeUnit.MINUTES);
public boolean runTest() {
try {
HttpResponse resp = Request.get("https://www.baidu.com")
.connectTimeout(timeout)
.responseTimeout(timeout)
.execute().returnResponse();
return resp.getCode() == 200;
} catch (IOException e) {
System.out.println(e.getMessage());
}
return false;
}
}

线程执行结果

package com.architect.stud;
/**
* 测试任务返回结果
* @author xiongbo
*
*/
public class TaskResult {
/**
* 响应时长
*/
private Long responseTime = 0L;
/**
* 是否成功
*/
private boolean isOK = true;
public Long getResponseTime() {
return responseTime;
}
public void setResponseTime(Long responseTime) {
this.responseTime = responseTime;
}
public boolean isOK() {
return isOK;
}
public void setOK(boolean isOK) {
this.isOK = isOK;
}
private TaskResult(Long responseTime,boolean isOK) {
this.responseTime = responseTime;
this.isOK = isOK;
}
public static TaskResult success(Long responseTime) {
return new TaskResult(responseTime,true);
}
public static TaskResult fail(Long responseTime) {
return new TaskResult(responseTime,false);
}
}



批次任务执行结果

package com.architect.stud;
/**
* 测试结果
* @author xiongbo
*
*/
public class TestReport {
/**
* 返回200的数量
*/
private int success=0;
/**
* 超时或者其他的数量
*/
private int fail = 0;
/**
* 当前第几次
*/
private int curFrequency = 0;
/**
* 平均响应时间
*/
private Double averageTime = 0.0;
private long maxTime = 0;
private long minTime = 0;
public int getSuccess() {
return success;
}
public TestReport setSuccess(int success) {
this.success = success;
return this;
}
public int getFail() {
return fail;
}
public TestReport setFail(int fail) {
this.fail = fail;
return this;
}
public Double getAverageTime() {
return averageTime;
}
public TestReport setAverageTime(double averageTime) {
this.averageTime = averageTime;
return this;
}
public int getCurFrequency() {
return curFrequency;
}
public TestReport setCurFrequency(int curFrequency) {
this.curFrequency = curFrequency;
return this;
}
public long getMaxTime() {
return maxTime;
}
public TestReport setMaxTime(long maxTime) {
this.maxTime = maxTime;
return this;
}
public long getMinTime() {
return minTime;
}
public TestReport setMinTime(long minTime) {
this.minTime = minTime;
return this;
}
public TestReport() {
}
public String getRes() {
return String.format("%d:成功数:%d,失败数:%d,最小响应时间:%d,最大响应时间:%d,平均响应时间:%f", this.curFrequency,this.success,this.fail,this.minTime,this.maxTime,this.averageTime);
}
}

运行压力测试

public static void main(String[] args) {
//先跑10次预热
Pressure.build(10, 10, new TestBaidu()).Test();
//再跑100次计算结果
List<TestReport> list = Pressure.build(10, 100, new TestBaidu()).Test().getResult();
list.stream().forEach(item -> {
System.out.println(item.getRes());
});
int i = Double.valueOf(Math.floor(list.size() * 0.95)).intValue();
System.out.println("95%平均响应:"+list.get(i).getAverageTime());
System.exit(0);
}



用户头像

Bruce Xiong

关注

熊大 2017.10.18 加入

还未添加个人简介

评论 (1 条评论)

发布
用户头像
请加“极客大学架构师训练营”标签,便于分类
2020 年 07 月 22 日 18:06
回复
没有更多了
架构师训练营第七周作业