架构师训练营第七周作业
发布于: 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.60000079:成功数:10,失败数:0,最小响应时间:32,最大响应时间:38,平均响应时间:34.80000071:成功数:10,失败数:0,最小响应时间:33,最大响应时间:38,平均响应时间:35.00000064:成功数:10,失败数:0,最小响应时间:33,最大响应时间:38,平均响应时间:35.20000066:成功数:10,失败数:0,最小响应时间:34,最大响应时间:38,平均响应时间:35.20000069:成功数:10,失败数:0,最小响应时间:33,最大响应时间:39,平均响应时间:35.20000084:成功数:10,失败数:0,最小响应时间:33,最大响应时间:39,平均响应时间:35.30000096:成功数:10,失败数:0,最小响应时间:33,最大响应时间:40,平均响应时间:35.40000051:成功数:10,失败数:0,最小响应时间:33,最大响应时间:40,平均响应时间:35.60000089:成功数:10,失败数:0,最小响应时间:33,最大响应时间:41,平均响应时间:36.00000097:成功数:10,失败数:0,最小响应时间:34,最大响应时间:42,平均响应时间:36.10000023:成功数:10,失败数:0,最小响应时间:33,最大响应时间:42,平均响应时间:36.20000044:成功数:10,失败数:0,最小响应时间:32,最大响应时间:43,平均响应时间:36.30000059:成功数:10,失败数:0,最小响应时间:33,最大响应时间:39,平均响应时间:36.30000081:成功数:10,失败数:0,最小响应时间:33,最大响应时间:39,平均响应时间:36.40000083:成功数:10,失败数:0,最小响应时间:33,最大响应时间:41,平均响应时间:36.40000035:成功数:10,失败数:0,最小响应时间:33,最大响应时间:42,平均响应时间:36.50000021:成功数:10,失败数:0,最小响应时间:33,最大响应时间:42,平均响应时间:36.60000052:成功数:10,失败数:0,最小响应时间:33,最大响应时间:54,平均响应时间:37.20000029:成功数:10,失败数:0,最小响应时间:33,最大响应时间:46,平均响应时间:37.30000036:成功数:10,失败数:0,最小响应时间:34,最大响应时间:43,平均响应时间:37.30000087:成功数:10,失败数:0,最小响应时间:34,最大响应时间:43,平均响应时间:37.300000100:成功数:10,失败数:0,最小响应时间:33,最大响应时间:44,平均响应时间:37.40000053:成功数:10,失败数:0,最小响应时间:34,最大响应时间:43,平均响应时间:37.60000056:成功数:10,失败数:0,最小响应时间:35,最大响应时间:41,平均响应时间:37.60000041:成功数:10,失败数:0,最小响应时间:33,最大响应时间:46,平均响应时间:37.70000061:成功数:10,失败数:0,最小响应时间:33,最大响应时间:43,平均响应时间:37.70000070:成功数:10,失败数:0,最小响应时间:33,最大响应时间:47,平均响应时间:38.00000050:成功数:10,失败数:0,最小响应时间:32,最大响应时间:43,平均响应时间:38.10000099:成功数:10,失败数:0,最小响应时间:34,最大响应时间:44,平均响应时间:38.40000055:成功数:10,失败数:0,最小响应时间:33,最大响应时间:45,平均响应时间:38.60000026:成功数:10,失败数:0,最小响应时间:33,最大响应时间:45,平均响应时间:38.90000031:成功数:10,失败数:0,最小响应时间:32,最大响应时间:45,平均响应时间:38.90000020:成功数:10,失败数:0,最小响应时间:33,最大响应时间:49,平均响应时间:39.1000001:成功数:10,失败数:0,最小响应时间:37,最大响应时间:44,平均响应时间:39.50000058:成功数:10,失败数:0,最小响应时间:34,最大响应时间:48,平均响应时间:39.60000025:成功数:10,失败数:0,最小响应时间:34,最大响应时间:45,平均响应时间:39.7000003:成功数:10,失败数:0,最小响应时间:36,最大响应时间:46,平均响应时间:39.90000010:成功数:10,失败数:0,最小响应时间:38,最大响应时间:44,平均响应时间:40.0000009:成功数:10,失败数:0,最小响应时间:38,最大响应时间:42,平均响应时间:40.10000057:成功数:10,失败数:0,最小响应时间:36,最大响应时间:53,平均响应时间:40.40000090:成功数:10,失败数:0,最小响应时间:34,最大响应时间:51,平均响应时间:40.50000024:成功数:10,失败数:0,最小响应时间:34,最大响应时间:59,平均响应时间:40.70000065:成功数:10,失败数:0,最小响应时间:33,最大响应时间:48,平均响应时间:40.70000016:成功数:10,失败数:0,最小响应时间:38,最大响应时间:44,平均响应时间:40.80000034:成功数:10,失败数:0,最小响应时间:33,最大响应时间:50,平均响应时间:40.90000072:成功数:10,失败数:0,最小响应时间:32,最大响应时间:49,平均响应时间:40.90000012:成功数:10,失败数:0,最小响应时间:38,最大响应时间:45,平均响应时间:41.00000028:成功数:10,失败数:0,最小响应时间:33,最大响应时间:57,平均响应时间:41.00000048:成功数:10,失败数:0,最小响应时间:33,最大响应时间:52,平均响应时间:41.10000063:成功数:10,失败数:0,最小响应时间:33,最大响应时间:67,平均响应时间:41.10000094:成功数:10,失败数:0,最小响应时间:35,最大响应时间:48,平均响应时间:41.10000039:成功数:10,失败数:0,最小响应时间:34,最大响应时间:59,平均响应时间:41.40000092:成功数:10,失败数:0,最小响应时间:34,最大响应时间:47,平均响应时间:41.40000043:成功数:10,失败数:0,最小响应时间:34,最大响应时间:55,平均响应时间:41.50000085:成功数:10,失败数:0,最小响应时间:34,最大响应时间:59,平均响应时间:41.60000032:成功数:10,失败数:0,最小响应时间:34,最大响应时间:58,平均响应时间:41.80000054:成功数:10,失败数:0,最小响应时间:36,最大响应时间:54,平均响应时间:42.10000077:成功数:10,失败数:0,最小响应时间:33,最大响应时间:110,平均响应时间:42.20000030:成功数:10,失败数:0,最小响应时间:34,最大响应时间:54,平均响应时间:42.30000073:成功数:10,失败数:0,最小响应时间:34,最大响应时间:89,平均响应时间:42.50000088:成功数:10,失败数:0,最小响应时间:38,最大响应时间:46,平均响应时间:42.60000038:成功数:10,失败数:0,最小响应时间:35,最大响应时间:55,平均响应时间:42.80000046:成功数:10,失败数:0,最小响应时间:33,最大响应时间:58,平均响应时间:43.10000060:成功数:10,失败数:0,最小响应时间:33,最大响应时间:59,平均响应时间:43.10000067:成功数:10,失败数:0,最小响应时间:33,最大响应时间:65,平均响应时间:43.30000091:成功数:10,失败数:0,最小响应时间:36,最大响应时间:52,平均响应时间:43.40000027:成功数:10,失败数:0,最小响应时间:34,最大响应时间:58,平均响应时间:43.60000076:成功数:10,失败数:0,最小响应时间:34,最大响应时间:53,平均响应时间:43.60000045:成功数:10,失败数:0,最小响应时间:37,最大响应时间:66,平均响应时间:43.70000015:成功数:10,失败数:0,最小响应时间:37,最大响应时间:58,平均响应时间:44.30000093:成功数:10,失败数:0,最小响应时间:33,最大响应时间:62,平均响应时间:44.60000098:成功数:10,失败数:0,最小响应时间:35,最大响应时间:62,平均响应时间:45.30000040:成功数:10,失败数:0,最小响应时间:34,最大响应时间:59,平均响应时间:45.70000080:成功数:10,失败数:0,最小响应时间:34,最大响应时间:62,平均响应时间:45.90000062:成功数:10,失败数:0,最小响应时间:34,最大响应时间:118,平均响应时间:46.90000068:成功数:10,失败数:0,最小响应时间:33,最大响应时间:139,平均响应时间:47.30000047:成功数:10,失败数:0,最小响应时间:34,最大响应时间:64,平均响应时间:48.2000002:成功数:10,失败数:0,最小响应时间:38,最大响应时间:64,平均响应时间:48.5000007:成功数:10,失败数:0,最小响应时间:42,最大响应时间:55,平均响应时间:49.30000074:成功数:10,失败数:0,最小响应时间:34,最大响应时间:133,平均响应时间:49.30000095:成功数:10,失败数:0,最小响应时间:36,最大响应时间:66,平均响应时间:49.40000011:成功数:10,失败数:0,最小响应时间:38,最大响应时间:58,平均响应时间:49.50000082:成功数:10,失败数:0,最小响应时间:36,最大响应时间:63,平均响应时间:49.80000013:成功数:10,失败数:0,最小响应时间:37,最大响应时间:108,平均响应时间:51.60000017:成功数:10,失败数:0,最小响应时间:41,最大响应时间:66,平均响应时间:51.9000005:成功数:10,失败数:0,最小响应时间:44,最大响应时间:63,平均响应时间:52.20000037:成功数:10,失败数:0,最小响应时间:35,最大响应时间:73,平均响应时间:52.40000022:成功数:10,失败数:0,最小响应时间:33,最大响应时间:90,平均响应时间:53.1000006:成功数:10,失败数:0,最小响应时间:40,最大响应时间:71,平均响应时间:53.90000049:成功数:10,失败数:0,最小响应时间:34,最大响应时间:215,平均响应时间:55.80000075:成功数:10,失败数:0,最小响应时间:33,最大响应时间:232,平均响应时间:56.10000019:成功数:10,失败数:0,最小响应时间:35,最大响应时间:168,平均响应时间:56.30000033:成功数:10,失败数:0,最小响应时间:40,最大响应时间:70,平均响应时间:59.90000018:成功数:10,失败数:0,最小响应时间:41,最大响应时间:237,平均响应时间:65.00000014:成功数:10,失败数:0,最小响应时间:37,最大响应时间:183,平均响应时间:69.5000004:成功数:10,失败数:0,最小响应时间:37,最大响应时间:272,平均响应时间:72.0000008:成功数:10,失败数:0,最小响应时间:40,最大响应时间:198,平均响应时间:73.30000078:成功数:10,失败数:0,最小响应时间:35,最大响应时间:275,平均响应时间:74.50000042:成功数:10,失败数:0,最小响应时间:38,最大响应时间:230,平均响应时间:84.70000095%平均响应: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); }
划线
评论
复制
发布于: 2020 年 07 月 22 日阅读数: 49
Bruce Xiong
关注
熊大 2017.10.18 加入
还未添加个人简介
评论 (1 条评论)