【第七周】命题作业——性能压测工具
发布于: 2020 年 07 月 19 日
以下两题,至少选做一题
性能压测的时候,随着并发压力的增加,系统响应时间和吞吐量如何变化,为什么?
用你熟悉的编程语言写一个 web 性能压测工具,输入参数:URL,请求总次数,并发数。输出参数:平均响应时间,95% 响应时间。用这个测试工具以 10 并发、100 次请求压测 www.baidu.com。
答:
1、并发增加时响应时间和吞吐量变化
第一阶段:系统资源利用率都在指标范围内
响应时间基本保持稳定,吞吐量逐步放大。
第二阶段:系统资源利用率超过指标,逐步到达瓶颈(最大值)
响应时间会有所延长,吞吐量继续放大,但放大的速度逐渐减缓。
第三阶段:系统资源超过瓶颈
响应时间快速延长,甚至超时,吞吐量迅速降低。
2、Web性能压测工具
1、压测核心工具类
提供压力并发和计数统计等功能
package xyz.hs.geek.training.week7;import java.util.concurrent.*;/** * @author huangsui * Created on 2020/7/20 */public class StressTestingUtils { public static StressResult testUrl(int totalRequest, int concurrencyLevel, String url) { return test(totalRequest, concurrencyLevel, new HttpTask(url)); } public static StressResult test(int totalRequest, int concurrencyLevel, Task task) { StressResult result = new StressResult(totalRequest, concurrencyLevel); long start = System.currentTimeMillis();// ExecutorService executorService = Executors.newFixedThreadPool(nThreads); ExecutorService executorService = new ThreadPoolExecutor(concurrencyLevel, concurrencyLevel, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(totalRequest)); Semaphore semaphore = new Semaphore(concurrencyLevel);// 控制最大并发数 CountDownLatch countDownLatch = new CountDownLatch(totalRequest);// 并发计数等待 CyclicBarrier cyclicBarrier = new CyclicBarrier(concurrencyLevel); for (int i = 0; i < totalRequest; i++) { executorService.execute(() -> { try { semaphore.acquire();// 控制最大并发数 try {// cyclicBarrier.await();// 集合完成后才出发 long startOfReq = System.currentTimeMillis(); boolean success = task.run(); long endOfReq = System.currentTimeMillis(); if(success){ result.complete(1); result.cost(endOfReq - startOfReq); }else { result.fail(1); } } catch (Exception e) { e.printStackTrace(); result.fail(1); } finally { countDownLatch.countDown(); } } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); } }); } try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } executorService.shutdown(); long end = System.currentTimeMillis(); result.setTotalTime(end - start); return result.stats(); }}
这里线程池和Semaphore都提供了最大并发数的控制。
2、压测结果类
package xyz.hs.geek.training.week7;import lombok.Getter;import lombok.Setter;import lombok.ToString;import java.util.ArrayList;import java.util.Comparator;import java.util.List;import java.util.concurrent.atomic.AtomicInteger;/** * @author huangsui * Created on 2020/7/21 */@Getter@Setter@ToStringpublic class StressResult { private final int totalRequest; private final int concurrencyLevel; private final List<Long> reqTimeList; private long totalCost; // 请求花费时间总和 private long totalTime; // 测试时间 private AtomicInteger completed = new AtomicInteger(0); private AtomicInteger failed = new AtomicInteger(0); private double qps;/*Requests per second*/ private double avgTime; private long shortestTime; private long fiftyTime; private long ninetyFiveTime; private long hundredTime; public StressResult(int totalRequest, int concurrencyLevel) { this.totalRequest = totalRequest; this.concurrencyLevel = concurrencyLevel; this.reqTimeList = new ArrayList<>(totalRequest); totalCost = 0L; } public void complete(int num){ completed.addAndGet(num); } public void cost(long cost){ reqTimeList.add(cost); totalCost += cost; } public void fail(int num){ failed.addAndGet(num); } public StressResult stats(){ this.avgTime = totalCost/completed.get(); this.qps = totalRequest/totalCost; reqTimeList.sort(Comparator.comparingLong(Long::longValue)); ninetyFiveTime = reqTimeList.get(Double.valueOf(reqTimeList.size()*0.95).intValue()); return this; }}
3、任务类
定义和实现具体任务
package xyz.hs.geek.training.week7;/** * @author huangsui * Created on 2020/7/21 */public interface Task { boolean run() throws Exception;}
package xyz.hs.geek.training.week7;import org.apache.http.client.methods.CloseableHttpResponse;import org.apache.http.client.methods.HttpGet;import org.apache.http.client.methods.HttpPost;import org.apache.http.client.methods.HttpUriRequest;import org.apache.http.client.utils.URIBuilder;import org.apache.http.entity.ContentType;import org.apache.http.entity.StringEntity;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.HttpClients;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import java.net.URI;import java.net.URISyntaxException;import java.util.Map;import java.util.concurrent.BrokenBarrierException;/** * @author huangsui * Created on 2020/7/21 */public class HttpTask implements Task { private final static Logger logger = LoggerFactory.getLogger(HttpTask.class); private String url; public HttpTask(String url) { this.url = url; } @Override public boolean run() throws URISyntaxException, BrokenBarrierException, InterruptedException { return doRequest(createHttpGet(url, null)); } private HttpUriRequest createHttpGet(String url, Map<String, String> param) throws URISyntaxException { URIBuilder builder = new URIBuilder(url); if (param != null) { for (String key : param.keySet()) { builder.addParameter(key, param.get(key)); } } URI uri = builder.build(); return new HttpGet(uri); } private HttpUriRequest createHttpPost(String url, String json){ HttpPost httpPost = new HttpPost(url); httpPost.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON)); return httpPost; } public boolean doRequest(HttpUriRequest httpUriRequest) { boolean result = true; try(CloseableHttpClient httpClient = HttpClients.createDefault()) {// logger.info("发起请求:{}", httpUriRequest.getURI()); try (CloseableHttpResponse response = httpClient.execute(httpUriRequest)){ if(response.getStatusLine().getStatusCode() != 200){ result = false; }// String respStr = EntityUtils.toString(response.getEntity(), "UTF-8");// logger.info("收到响应:{},{}",response.getStatusLine().getStatusCode(), respStr); } } catch (Exception e) { logger.error("请求异常:{}", e.toString()); result = false; } return result; }}
4、进行压测
package xyz.hs.geek.training;import org.junit.Test;import xyz.hs.geek.training.week7.StressResult;import xyz.hs.geek.training.week7.StressTestingUtils;/** * @author huangsui * Created on 2020/7/21 */public class StressTestingUtilsTests { @Test public void test(){ StressResult result = StressTestingUtils.testUrl(100, 10, "http://www.baidu.com");// System.out.println(result); System.out.printf("总请求数:%d \n", 100); System.out.printf("并发数:%d \n", 10); System.out.printf("平均响应时间:%f ms\n", result.getAvgTime()); System.out.printf("95%%响应时间:%d ms\n", result.getNinetyFiveTime()); }}
压测输出结果:
平均响应时间:372ms,95%响应时间:2920ms
划线
评论
复制
发布于: 2020 年 07 月 19 日 阅读数: 46
三尾鱼
关注
还未添加个人签名 2018.07.10 加入
还未添加个人简介
评论