架构师训练营 -week7 web 性能压测工具
发布于: 2020 年 07 月 20 日
题目
用你熟悉的编程语言写一个 web 性能压测工具,输入参数:URL,请求总次数,并发数。输出参数:平均响应时间,95% 响应时间。用这个测试工具以 10 并发、100 次请求压测 www.baidu.com。
实现
HttpClient请求类
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.net.HttpURLConnection;import java.net.MalformedURLException;import java.net.ProtocolException;import java.net.URL;import java.nio.charset.StandardCharsets;import java.util.Base64;import java.util.Map;import java.util.function.BiConsumer;import java.util.function.Consumer;import java.util.function.Function;import java.util.stream.Collectors;import java.util.zip.GZIPInputStream;public class HttpClient { public static String doGet(String url,boolean isGzip) { final HttpURLConnection httpURLConnection = getHttpURLConnection(url); if(isGzip){ setGizp.accept(httpURLConnection); } setDefaultUserAgent.accept(httpURLConnection); setMedthod.accept(httpURLConnection,"GET"); return getResponse(httpURLConnection,isGzip, HttpClient::byteStream2string); } public static HttpURLConnection getHttpURLConnection(String strUrl) { try { // 创建远程url连接对象 URL url = new URL(strUrl); // 通过远程url连接对象打开一个连接,强转成httpURLConnection类 return (HttpURLConnection) url.openConnection(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } public final static BiConsumer<HttpURLConnection, String> setMedthod = (httpURLConnection, method) -> { try { httpURLConnection.setRequestMethod(method); } catch (ProtocolException e) { e.printStackTrace(); } }; public final static BiConsumer<HttpURLConnection, Integer> setTimeout = (httpURLConnection, timeout) -> { httpURLConnection.setConnectTimeout(timeout * 1000); }; public final static BiConsumer<HttpURLConnection, Map<String, String>> setCookie = (httpURLConnection, cookieMap) -> { httpURLConnection.addRequestProperty("Cookie", cookieMap.keySet().stream() .map(key -> key + "=" + cookieMap.get(key)) .collect(Collectors.joining(";")) ); }; public final static BiConsumer<HttpURLConnection, String> setReferer = (httpURLConnection, s) -> { httpURLConnection.addRequestProperty("referer", s); }; public final static BiConsumer<HttpURLConnection, String> setUserAgent = (httpURLConnection, s) -> { httpURLConnection.addRequestProperty("User-Agent",s); }; public final static Consumer<HttpURLConnection> setDefaultUserAgent = httpURLConnection -> setUserAgent.accept(httpURLConnection,"Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Mobile Safari/537.36"); public final static Consumer<HttpURLConnection> setGizp = httpURLConnection -> httpURLConnection.addRequestProperty("Accept-Encoding","gzip, deflate, br"); public static <T> T getResponse(HttpURLConnection httpURLConnection, Boolean enableGzip, Function<InputStream,T> callback) { try{ InputStream inputStream = httpURLConnection.getInputStream(); boolean isGizp = enableGzip || "gzip".equals(httpURLConnection.getHeaderField("Content-Encoding")); if (isGizp) { inputStream = new GZIPInputStream(inputStream); } int responseCode = httpURLConnection.getResponseCode(); if(responseCode != 200){ throw new RuntimeException(responseCode + " 请求异常"); } if (callback == null) { inputStream.close(); } else { return callback.apply(inputStream); } } catch (Exception e) { return null; } return null; } public static String byteStream2string(InputStream inputStream) { StringBuilder result = new StringBuilder(); try (InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); BufferedReader reader = new BufferedReader(inputStreamReader); ) { String line = null; while ((line = reader.readLine()) != null) { result.append(line); result.append("\r\n"); } return result.toString(); } catch (IOException e) { return null; } }}
WebTest 测试类
import java.util.ArrayList;import java.util.List;import java.util.Optional;import java.util.concurrent.*;import java.util.concurrent.atomic.AtomicInteger;import java.util.stream.Collectors;public class WebTest { public static void main(String[] args) throws InterruptedException { if(args.length != 3 ){ throw new IllegalArgumentException("请输入参数:URL,请求总次数,并发数"); } System.out.println("URL:"+args[0]); System.out.println("请求总次数:"+args[1]); System.out.println("并发数:"+args[2]); String url = args[0]; int totalCount = Integer.valueOf(args[1]); int concurrencyCount = Integer.valueOf(args[2]); AtomicInteger totalRows = new AtomicInteger(0); AtomicInteger failRows = new AtomicInteger(0); // 创建线程池,其中核心线程10,也是最大并发数,最大线程数和队列大小都为100,即总任务数 ThreadPoolExecutor executor = new ThreadPoolExecutor(concurrencyCount, totalCount, 1, TimeUnit.SECONDS, new ArrayBlockingQueue<>(totalCount)); List<Long> respList = new ArrayList<>(totalCount); // 初始化CountDownLatch,大小为totalCount CountDownLatch countDownLatch = new CountDownLatch(totalCount); // 模拟遍历参数集合 for (int i = 0; i < totalCount; i++) { // 往线程池提交任务 executor.execute(new Runnable() { @Override public void run() { long startTime = System.currentTimeMillis(); String result = HttpClient.doGet(url, true); long costTime = System.currentTimeMillis()- startTime; respList.add(costTime); if(result == null){ failRows.incrementAndGet(); } totalRows.incrementAndGet(); // 子线程完成,countDownLatch执行countDown countDownLatch.countDown(); } }); // 打印线程池运行状态// System.out.println("线程池中线程数目:" + executor.getPoolSize() + ",队列中等待执行的任务数目:" +// executor.getQueue().size() + ",已执行结束的任务数目:" + executor.getCompletedTaskCount()); } // 标记多线程关闭,但不会立马关闭 executor.shutdown(); // 阻塞当前线程,直到所有子线程都执行countDown方法才会继续执行 countDownLatch.await(); // 打印线程池运行状态// System.out.println("线程池中线程数目:" + executor.getPoolSize() + ",队列中等待执行的任务数目:" +// executor.getQueue().size() + ",已执行结束的任务数目:" + executor.getCompletedTaskCount()); // 总用时 long sumTime = Optional.ofNullable(respList).orElse(new ArrayList<>()).stream() .mapToLong(Long::longValue).sum(); // 平均响应时间 long avgTime = Optional.ofNullable(respList).orElse(new ArrayList<>()).stream() .map(Long::longValue) .collect(Collectors.averagingLong(Long::longValue)).longValue(); // 95%响应时间 long tt95AvgTime = Optional.ofNullable(respList).get() .stream() .sorted() .collect(Collectors.toList()) .get(Math.max(Math.floorDiv(respList.size()*95,100)-1,0)); // 计数 System.out.println("压测结束"); System.out.println("总用时ms:" + sumTime); System.out.println("总请求数:" + totalRows.get()); System.out.println("失败数:" + failRows.get()); System.out.println("成功数:" + (totalRows.get() - failRows.get())); System.out.println("平均响应时间ms:"+avgTime); System.out.println("95%响应时间ms:"+tt95AvgTime); }}
测试结果
/Users/chenlei/private/designpattern/out/production/designpattern com.chenlei.demo.webtest.WebTest https://www.baidu.com 100 10URL:https://www.baidu.com请求总次数:100并发数:10压测结束总用时ms:36536总请求数:100失败数:0成功数:100平均响应时间ms:36595%响应时间ms:1215Process finished with exit code 0
划线
评论
复制
发布于: 2020 年 07 月 20 日 阅读数: 33

尔东雨田
关注
预备用枪! 2017.12.12 加入
还未添加个人简介
评论