写点什么

架构师训练营第 7 周作业

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

题目

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



思路

参照ab的实现写一个基于命令行的压测工具,使用HttpClient连接服务器,采用线程池限制并发数量。以下为代码实现:

package cn.edana.benchmark;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.DefaultParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BenchmarkApplication implements CommandLineRunner {
public static final String OPT_REQUEST = "n";
public static final String OPT_CONCURRENCY = "c";
public static final String OPT_TIMELIMIT = "t";
public static final String OPT_TIMEOUT = "s";
public static void main(String[] args) {
SpringApplication.run(BenchmarkApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
/* Build command line */
Options options = buildCommandLineOptions();
/* Parse command line */
CommandLine result = parseCommandLine(args, options);
int times = Integer.parseInt(result.getOptionValue(OPT_REQUEST));
int concurrency = Integer.parseInt(result.getOptionValue(OPT_CONCURRENCY));
int timeout = Integer.parseInt(result.getOptionValue(OPT_TIMEOUT));
String url = result.getArgList().get(result.getArgList().size() - 1);
BenchmarkExecutor benchmarkExecutor = new BenchmarkExecutor(url, concurrency, timeout, times);
final BenchmarkResult r = benchmarkExecutor.execute();
System.out.println(String.format("总共压测%d次, 成功%d次,平均响应时间%dms, 95响应时间%dms", r.getTotal(), r.getSuccess(), r.getAvgRT(), r.get_95thRT()));
}
private Options buildCommandLineOptions() {
Options options = new Options();
options.addOption(Option.builder(OPT_REQUEST)
.hasArg()
.argName("request")
.desc("Number of requests to perform")
.build());
options.addOption(Option.builder(OPT_CONCURRENCY)
.hasArg()
.argName("concurrency")
.desc("Number of multiple requests to make at a time")
.build());
options.addOption(Option.builder(OPT_TIMELIMIT)
.hasArg()
.argName("timelimit")
.desc("Seconds to max. to spend on benchmarking. This implies -n 50000")
.build());
options.addOption(Option.builder(OPT_TIMEOUT)
.hasArg()
.argName("timeout")
.desc("Seconds to max. wait for each response. Default is 30 seconds")
.build());
options.addOption(Option.builder("h")
.desc("help for bt")
.build());
return options;
}
private CommandLine parseCommandLine(String[] args, Options options) {
CommandLineParser parser = new DefaultParser();
CommandLine result = null;
try {
result = parser.parse(options, args);
} catch (ParseException e) {
System.err.println(e.getMessage());
printHelp(options);
}
if (result.getOptions().length == 0 || result.hasOption("h")) {
printHelp(options);
}
return result;
}
private void printHelp(Options options) {
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("java -jar bt.jar [options] <url>\n\noptions: ", options, false);
System.exit(1);
}
}



package cn.edana.benchmark;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeoutException;
import java.util.function.Supplier;
import java.util.stream.Stream;
public class BenchmarkExecutor {
private ExecutorService executorService;
private String url;
private int timeout;
private int times;
public BenchmarkExecutor(String url, int concurrency, int timeout, int times) {
this.url = url;
this.timeout = timeout;
this.times = times;
this.executorService = new ScheduledThreadPoolExecutor(concurrency,
new BasicThreadFactory.Builder().namingPattern("benchmark-schedule-pool-%d").daemon(true).build());
}
public BenchmarkResult execute() throws InterruptedException, ExecutionException, TimeoutException {
CompletionService cs = new ExecutorCompletionService<>(executorService);
RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(timeout*1000).setConnectTimeout(timeout*1000).build();
for (int i = 0; i < times; i++) {
final HttpGet request = new HttpGet(this.url);
request.setConfig(requestConfig);
cs.submit(new BenchmarkTask(HttpClients.createDefault(), request));
}
List<BenchmarkTaskResult> results = new ArrayList<>(times);
for (int i = 0; i< times; i++ ){
results.add((BenchmarkTaskResult) cs.take().get());
}
Supplier<Stream<BenchmarkTaskResult>> supplier = () -> results.stream().filter(r -> r.getStatus() == 200);
long success = supplier.get().count();
long avgRT = supplier.get().mapToLong(r -> r.getResponseTime()).sum() / times;
final long _95RT = supplier.get().sorted((r1, r2) -> {
if (r1.getResponseTime() > r2.getResponseTime()) {
return 1;
}
if (r1.getResponseTime() < r2.getResponseTime()) {
return -1;
}
return 0;
}).skip((long) (times * .95)).findFirst().get().getResponseTime();
return new BenchmarkResult(times, success, avgRT, _95RT);
}
}



package cn.edana.benchmark;
import lombok.Value;
@Value
public class BenchmarkResult {
private long total;
private long success;
private long avgRT;
private long _95thRT;
}



package cn.edana.benchmark;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.impl.client.CloseableHttpClient;
import java.util.concurrent.Callable;
public class BenchmarkTask implements Callable {
CloseableHttpClient httpClient;
HttpRequestBase request;
public BenchmarkTask(CloseableHttpClient httpClient, HttpRequestBase request) {
this.httpClient = httpClient;
this.request = request;
}
@Override
public BenchmarkTaskResult call() throws Exception {
long t = System.currentTimeMillis();
final CloseableHttpResponse response = httpClient.execute(request);
return new BenchmarkTaskResult(response.getStatusLine().getStatusCode(), System.currentTimeMillis() - t);
}
}



package cn.edana.benchmark;
import lombok.Value;
/**
* @author 郑喜荣
* @create 2020-07-22 7:45 下午
**/
@Value
public class BenchmarkTaskResult {
private int status;
private long responseTime;
}



执行结果



完整代码请参考:https://github.com/manekicn/benchmark-test

用户头像

Season

关注

还未添加个人签名 2019.09.28 加入

还未添加个人简介

评论

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