架构师训练营第 7 周作业
发布于: 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;@SpringBootApplicationpublic 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;@Valuepublic 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 下午 **/@Valuepublic class BenchmarkTaskResult { private int status; private long responseTime;}
执行结果
划线
评论
复制
发布于: 2020 年 07 月 22 日阅读数: 62
Season
关注
还未添加个人签名 2019.09.28 加入
还未添加个人简介
评论