架构师训练营第七周作业一
发布于: 2020 年 07 月 22 日
性能压测的时候,随着并发压力的增加,系统响应时间和吞吐量如何变化,为什么?
一个系统吞吐量通常由 QPS(TPS)、并发数两个因素决定,每套系统这两个值都有一个相对极限值,在应用场景访问压力下,只要某一项达到系统最高值,系统的吞吐量就上不去了,如果压力继续增大,系统的吞吐量反而会下降。
假设并发数不变:QPS不断增加,QPS超过最大吞吐量后,会有大量请求等待,平均响应时间急剧下降
假设 QPS 不变: 增加并发数,会导致CPU并发线程过多,线上上下文切换频繁,内存消耗增加,从而使平均响应时间下降
用你熟悉的编程语言写一个 web 性能压测工具,输入参数:URL,请求总次数,并发数。输出参数:平均响应时间,95% 响应时间。用这个测试工具以 10 并发、100 次请求压测 www.baidu.com。
类图
IHttpRequestRunner 类
public interface IHttpRequestRunner { void init(HttpClientProperties httpClientProperties); HttpRequestResult get(String url);}
OkHttpRequestRunner
public class OkHttpRequestRunner implements IHttpRequestRunner { private OkHttpClient okHttpClient; private boolean initialized = false; public synchronized void init(HttpClientProperties httpClientProperties) { if (!initialized) { ConnectionPool connectionPool = new ConnectionPool(); this.okHttpClient = new OkHttpClient().newBuilder() .connectionPool(connectionPool) .connectTimeout(httpClientProperties.getConnectTimeout(), TimeUnit.SECONDS) .readTimeout(httpClientProperties.getReadTimeout(), TimeUnit.SECONDS) .build(); initialized = true; } } public HttpRequestResult get(String url) { Request request = new Request.Builder().url(url).build(); try { Response response = okHttpClient.newCall(request).execute(); return new HttpRequestResult(true, new String(response.body().bytes())); } catch (IOException e) { e.printStackTrace(); return new HttpRequestResult(false); } }}
HttpRequestRunnerDecorator 类
public class HttpRequestRunnerDecorator implements IHttpRequestRunner { private final IHttpRequestRunner httpRequestRunner; public HttpRequestRunnerDecorator(IHttpRequestRunner httpRequestRunner) { this.httpRequestRunner = httpRequestRunner; } public void init(HttpClientProperties httpClientProperties) { httpRequestRunner.init(httpClientProperties); } public HttpRequestResult get(String url) { long startTs = System.currentTimeMillis(); HttpRequestResult httpRequestResult = httpRequestRunner.get(url); long endTs = System.currentTimeMillis(); httpRequestResult.setElapseTime(endTs - startTs); return httpRequestResult; }}
HttpClientProperties
public class HttpClientProperties { private final long connectTimeout; private final long readTimeout; public HttpClientProperties(long connectTimeout, long readTimeout) { this.connectTimeout = connectTimeout; this.readTimeout = readTimeout; } public long getConnectTimeout() { return connectTimeout; } public long getReadTimeout() { return readTimeout; }}
HttpRequestResult
public class HttpRequestResult { private final boolean success; private String content; private long elapseTime; public HttpRequestResult(boolean success, String content) { this.success = success; this.content = content; } public HttpRequestResult(boolean success) { this.success = success; } public String getContent() { return content; } public boolean isSuccess() { return success; } public long getElapseTime() { return elapseTime; } public void setElapseTime(long elapseTime) { this.elapseTime = elapseTime; }}
IWebBenchmarkFacade
public interface IWebBenchmarkFacade { WebBenchmarkResult execute(String url, int concurrency, long totalRequestTimes) throws BenchmarkExecuteException;}
WebBenchmarkResult
public class WebBenchmarkResult { private final double avgResponseTime; private final double _95ResponseTime; public WebBenchmarkResult(double avgResponseTime, double _95ResponseTime) { this.avgResponseTime = avgResponseTime; this._95ResponseTime = _95ResponseTime; } public double getAvgResponseTime() { return avgResponseTime; } public double get_95ResponseTime() { return _95ResponseTime; }}
WebBenchmarkFacadeTemplate
public abstract class WebBenchmarkFacadeTemplate implements IWebBenchmarkFacade { private final IHttpRequestRunner httpRequestRunner; private final HttpClientProperties httpClientProperties; public WebBenchmarkFacadeTemplate(IHttpRequestRunner httpRequestRunner, HttpClientProperties httpClientProperties) { this.httpRequestRunner = httpRequestRunner; this.httpClientProperties = httpClientProperties; } protected void warmUp(String url, int requestTimes) { for (int i = 0; i < requestTimes; i++) { this.httpRequestRunner.get(url); } } public WebBenchmarkResult execute(String url, int concurrency, long totalRequestTimes) throws BenchmarkExecuteException { this.setUp(); this.warmUp(url, concurrency); List<Thread> threads = new ArrayList<>(); List<BenchmarkTask> benchmarkTasks = new ArrayList<>(); long requestTimes = totalRequestTimes / concurrency; for (int i = 0; i < concurrency; i++) { BenchmarkTask benchmarkTask = new BenchmarkTask(httpRequestRunner, requestTimes, url); Thread thread = new Thread(benchmarkTask); thread.start(); benchmarkTasks.add(benchmarkTask); threads.add(thread); } for (Thread thread : threads) { try { thread.join(); } catch (InterruptedException e) { throw new BenchmarkExecuteException(e.getMessage()); } } List<HttpRequestResult> requestResults = new ArrayList<>(); for (BenchmarkTask benchmarkTask : benchmarkTasks) { requestResults.addAll(benchmarkTask.getResultSet()); } requestResults.sort(Comparator.comparingLong(HttpRequestResult::getElapseTime)); long totalElapse = 0; long[] elapseTimes = new long[requestResults.size()]; for (int i = 0; i < requestResults.size(); i++) { elapseTimes[i] = requestResults.get(i).getElapseTime(); totalElapse += requestResults.get(i).getElapseTime(); } return new WebBenchmarkResult(totalElapse / (double) elapseTimes.length, elapseTimes[(int) (elapseTimes.length * 0.95D)]); } private void setUp() { this.httpRequestRunner.init(httpClientProperties); } private static class BenchmarkTask implements Runnable { private final IHttpRequestRunner httpRequestRunner; private final long requestTimes; private final String url; private final Collection<HttpRequestResult> resultSet; public BenchmarkTask(IHttpRequestRunner httpRequestRunner, long requestTimes, String url) { this.httpRequestRunner = httpRequestRunner; this.requestTimes = requestTimes; this.url = url; this.resultSet = new ArrayList<>(); } public void run() { for (int i = 0; i < requestTimes; i++) { resultSet.add(httpRequestRunner.get(url)); } } public Collection<HttpRequestResult> getResultSet() { return resultSet; } }}
WebBenchmarkFacade
public class WebBenchmarkFacade extends WebBenchmarkFacadeTemplate { public WebBenchmarkFacade(IHttpRequestRunner httpRequestRunner, HttpClientProperties httpClientProperties) { super(httpRequestRunner, httpClientProperties); } @Override public WebBenchmarkResult execute(String url, int concurrency, long totalRequestTimes) throws BenchmarkExecuteException { return super.execute(url, concurrency, totalRequestTimes); }}
运行入口
public class CommandLineRunner { public static void main(String[] args) throws BenchmarkExecuteException { Options opt = new Options(); opt.addOption("c", true, "concurrency of benchmark task"); opt.addOption(Option.builder().longOpt("url").desc("target url of benchmark task").valueSeparator('=').hasArg().build()); opt.addOption(Option.builder().longOpt("total-request-count").desc("total request count of benchmark task").valueSeparator('=').hasArg().build()); opt.addOption("h", "help", false, "print help for the command"); String formatStr = "ewm [-c][--url][--total-request-count][-h/--help]"; HelpFormatter format = new HelpFormatter(); CommandLineParser parse = new DefaultParser(); CommandLine cli; try { cli = parse.parse(opt, args); } catch (ParseException e) { format.printHelp(formatStr, opt); return; } if (cli.hasOption("h")) { HelpFormatter hf = new HelpFormatter(); hf.printHelp(formatStr, "", opt, ""); return; } if (cli.hasOption("url")) { System.out.println("url: \t" + cli.getOptionValue("url")); } if (cli.hasOption("c")) { System.out.println("concurrency: \t " + cli.getOptionValue("c")); } if (cli.hasOption("total-request-count")) { System.out.println("total request count: \t" + cli.getOptionValue("total-request-count")); } IHttpRequestRunner httpRequestRunner = new HttpRequestRunnerDecorator(new OkHttpRequestRunner()); HttpClientProperties httpClientProperties = new HttpClientProperties(300, 30); IWebBenchmarkFacade webBenchmarkFacade = new WebBenchmarkFacade(httpRequestRunner, httpClientProperties); WebBenchmarkResult benchmarkResult = webBenchmarkFacade.execute(cli.getOptionValue("url"), Integer.parseInt(cli.getOptionValue("c")), Long.parseLong(cli.getOptionValue("total-request-count"))); System.out.println("average response time: \t" + benchmarkResult.getAvgResponseTime()); System.out.println("95 percentage response time: \t" + benchmarkResult.get_95ResponseTime()); }}
运行结果
url: https://www.baidu.comconcurrency: 10total request count: 100average response time: 70.5995 percentage response time: 121
划线
评论
复制
发布于: 2020 年 07 月 22 日 阅读数: 30
0x12FD16B
关注
还未添加个人签名 2018.01.19 加入
还未添加个人简介
评论