架构师训练营 -Week 07 命题作业
发布于: 2020 年 07 月 22 日
性能压测的时候,随着并发压力的增加,系统响应时间和吞吐量如何变化,为什么?
系统资源未饱和的情况下,随着并发压力的增加,系统的响应时间不变或缓慢增长,吞吐量上升;
到达系统资源饱和的点之后,随着并发压力的增加,系统的响应时间急剧增长,吞吐量下降;
因为CPU、磁盘/网络IO等系统资源是有限的,超过资源的消耗,会倒置系统的处理能力下降,甚至系统会崩溃不能正常运行。
用你熟悉的编程语言写一个 web 性能压测工具,输入参数:URL,请求总次数,并发数。输出参数:平均响应时间,95% 响应时间。用这个测试工具以 10 并发、100 次请求压测 www.baidu.com。
线程并发主要用了Executor框架中的自定义ThreadPoolExecutor线程池来实现,web请求实现了Callable接口返回响应时间,executor.invokeAll对多个线程任务统一返回处理请求结果。
采用了HttpURLCollection来对URL进行Web请求,本来打算用的是HttpClient或者Okhttp来实现web请求,但是底层实现有连接池,如果连接池没设计好可能会影响测试,所以还是用来单线程同步请求的HttpURLCollection类来实现。
ConcurrencyTestResult类:
/** * 并发测试结果 */public class ConcurrencyTestResult { /** * 平均响应时间(毫秒) */ private long averageResponseTime; /** * 95%响应时间(毫秒) */ private long ninetyFivePercentageResponseTime; /** * 最小响应时间(毫秒) */ private long minPercentageResponseTime; /** * 最大响应时间(毫秒) */ private long maxPercentageResponseTime; /** * 请求成功次数 */ private int requestSucceedNum; /** * 请求失败次数 */ private int requestFailedNum; public ConcurrencyTestResult() { } public ConcurrencyTestResult(long averageResponseTime, long ninetyFivePercentageResponseTime, long minPercentageResponseTime, long maxPercentageResponseTime, int requestSucceedNum, int requestFailedNum) { this.averageResponseTime = averageResponseTime; this.ninetyFivePercentageResponseTime = ninetyFivePercentageResponseTime; this.minPercentageResponseTime = minPercentageResponseTime; this.maxPercentageResponseTime = maxPercentageResponseTime; this.requestSucceedNum = requestSucceedNum; this.requestFailedNum = requestFailedNum; } public long getAverageResponseTime() { return averageResponseTime; } public void setAverageResponseTime(long averageResponseTime) { this.averageResponseTime = averageResponseTime; } public long getNinetyFivePercentageResponseTime() { return ninetyFivePercentageResponseTime; } public void setNinetyFivePercentageResponseTime(long ninetyFivePercentageResponseTime) { this.ninetyFivePercentageResponseTime = ninetyFivePercentageResponseTime; } public long getMinPercentageResponseTime() { return minPercentageResponseTime; } public void setMinPercentageResponseTime(long minPercentageResponseTime) { this.minPercentageResponseTime = minPercentageResponseTime; } public long getMaxPercentageResponseTime() { return maxPercentageResponseTime; } public void setMaxPercentageResponseTime(long maxPercentageResponseTime) { this.maxPercentageResponseTime = maxPercentageResponseTime; } public int getRequestSucceedNum() { return requestSucceedNum; } public void setRequestSucceedNum(int requestSucceedNum) { this.requestSucceedNum = requestSucceedNum; } public int getRequestFailedNum() { return requestFailedNum; } public void setRequestFailedNum(int requestFailedNum) { this.requestFailedNum = requestFailedNum; } public void print() { System.out.printf("平均响应时间: %s(ms)\n", averageResponseTime); System.out.printf("百分之95响应时间: %s(ms)\n", ninetyFivePercentageResponseTime); System.out.printf("最小响应时间: %s(ms)\n", minPercentageResponseTime); System.out.printf("最大响应时间: %s(ms)\n", maxPercentageResponseTime); System.out.printf("请求成功次数: %s次\n", requestSucceedNum); System.out.printf("请求失败次数: %s次\n", requestFailedNum); }}
RequestTask类:
/** * 请求任务 * */public class RequestTask implements Callable<RequestTaskResult> { private String url; private String method; public RequestTask(String url, String method) { this.url = url; this.method = method; } @Override public RequestTaskResult call() { HttpURLConnection connection = null; RequestTaskResult result = new RequestTaskResult(); Long responseTime = 0L; Boolean requestSuccess = false; try { long startTime = System.currentTimeMillis(); connection = (HttpURLConnection) new URL(url).openConnection(); connection.setRequestMethod(method); connection.connect(); int code = connection.getResponseCode(); if (code == 200) { responseTime = System.currentTimeMillis() - startTime; requestSuccess = true; } } catch (Exception e) { e.printStackTrace(); } finally { if (connection != null) { connection.disconnect(); } } result.setRequestSuccess(requestSuccess); result.setResponseTime(responseTime); return result; }}
RequestTaskResult类:
/** * 请求任务结果 * */public class RequestTaskResult { private Long responseTime; private Boolean requestSuccess; public Long getResponseTime() { return responseTime; } public void setResponseTime(Long responseTime) { this.responseTime = responseTime; } public Boolean getRequestSuccess() { return requestSuccess; } public void setRequestSuccess(Boolean requestSuccess) { this.requestSuccess = requestSuccess; }}
WebPerformanceTesting接口:
/** * 性能测试 */public interface WebPerformanceTesting { /** * 并发测试 * * @param url 请求URL * @param method 请求方式 * @param concurrencyNum 并发数 * @param requestTotal 请求总次数 */ public ConcurrencyTestResult concurrencyTest(String url, String method, int concurrencyNum, int requestTotal);}
WebPerformanceTestingImpl接口:
public class WebPerformanceTestingImpl implements WebPerformanceTesting { @Override public ConcurrencyTestResult concurrencyTest(String url, String method, int concurrencyNum, int requestTotal) { ThreadPoolExecutor executor = new ThreadPoolExecutor(concurrencyNum, concurrencyNum, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>()); List<RequestTask> taskList = new ArrayList<>(requestTotal); for (int i = 0;i < requestTotal; i++) { taskList.add(new RequestTask(url, method)); } long averageResponseTime = 0L; long ninetyFivePercentageResponseTime = 0L; long minPercentageResponseTime = 0L; long maxPercentageResponseTime = 0L; int requestSucceedNum = 0; int requestFailedNum = 0; try { List<Future<RequestTaskResult>> resultList = executor.invokeAll(taskList); if (resultList != null && !resultList.isEmpty()) { List<Long> responseTimeList = new ArrayList<>(); for (Future<RequestTaskResult> future : resultList) { RequestTaskResult result = future.get(); responseTimeList.add(result.getResponseTime()); if (result.getRequestSuccess()) { requestSucceedNum++; } } maxPercentageResponseTime = responseTimeList.stream().max(Long::compareTo).get(); minPercentageResponseTime = responseTimeList.stream().min(Long::compareTo).get(); averageResponseTime = responseTimeList.stream().reduce(Long::sum).get() / responseTimeList.size();//平均值 Collections.sort(responseTimeList); int index = new Double(responseTimeList.size() * 0.95).intValue() - 1; index = (index >= 0) ? index : 0; ninetyFivePercentageResponseTime = responseTimeList.get(index); requestFailedNum = requestTotal - requestSucceedNum; } } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } ConcurrencyTestResult concurrencyTestResult = new ConcurrencyTestResult( averageResponseTime, ninetyFivePercentageResponseTime, minPercentageResponseTime, maxPercentageResponseTime, requestSucceedNum, requestFailedNum ); return concurrencyTestResult; } public static void main(String[] args) { WebPerformanceTesting webPerformanceTesting = new WebPerformanceTestingImpl(); String url = "https://www.baidu.com"; String method = "GET"; int concurrencyNum = 10; int requestTotal = 100; ConcurrencyTestResult concurrencyTestResult = webPerformanceTesting.concurrencyTest(url, method, concurrencyNum, requestTotal); System.out.printf("请求URL: %s\n", url); System.out.printf("请求方法: %s\n", method); System.out.printf("并发数: %s\n", concurrencyNum); System.out.printf("请求总数: %s\n", requestTotal); concurrencyTestResult.print(); }}
测试结果:
请求URL: https://www.baidu.com请求方法: GET并发数: 10请求总数: 100平均响应时间: 92(ms)百分之95响应时间: 453(ms)最小响应时间: 34(ms)最大响应时间: 461(ms)请求成功次数: 100次请求失败次数: 0次
划线
评论
复制
发布于: 2020 年 07 月 22 日阅读数: 54
评论