架构师训练营第七周作业一

用户头像
0x12FD16B
关注
发布于: 2020 年 07 月 22 日
  • 性能压测的时候,随着并发压力的增加,系统响应时间和吞吐量如何变化,为什么?

一个系统吞吐量通常由 QPS(TPS)、并发数两个因素决定,每套系统这两个值都有一个相对极限值,在应用场景访问压力下,只要某一项达到系统最高值,系统的吞吐量就上不去了,如果压力继续增大,系统的吞吐量反而会下降。

  1. 假设并发数不变:QPS不断增加,QPS超过最大吞吐量后,会有大量请求等待,平均响应时间急剧下降

  2. 假设 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.com
concurrency: 10
total request count: 100
average response time: 70.59
95 percentage response time: 121



用户头像

0x12FD16B

关注

还未添加个人签名 2018.01.19 加入

还未添加个人简介

评论

发布
暂无评论
架构师训练营第七周作业一