1
性能测试
发布于: 2020 年 07 月 21 日
以下两题,至少选做一题
性能压测的时候,随着并发压力的增加,系统响应时间和吞吐量如何变化,为什么?
用你熟悉的编程语言写一个 web 性能压测工具,输入参数:URL,请求总次数,并发数。输出参数:平均响应时间,95% 响应时间。用这个测试工具以 10 并发、100 次请求压测 www.baidu.com。
一、性能测试,压力曲线
随着并发压力的增加,系统响应时间和吞吐量的变化,如上图所示。
开始,随着并发数的增加,cpu利用率上升,QPS相应增加,平均响应时间也在增加(变化不大),资源占用率和吞吐量会响应的增加。且平均响应时间是一个指数增加曲线。
当并发数达到一定程度时,资源占用率达到饱和,吞吐量增长放缓甚至停止增长,响应时间进一步延长。
当并发继续增长时,资源饱和,吞吐量下降,响应时间变很长。
这时候,资源达到瓶颈,每秒都会有很多请求需要处理,会造成进程或线程频繁切换,加大了上下文的切换时间,用于处理请求的时间也变少,也就每秒能够处理的请求数变小,响应时间变长,也就造成用户的等待时间变大。
二、java实现压测工具如下:
public class PerformTestTool { /** * 一般的性能测试工具按照该算法,但对本地来说不均衡 * 按照顺序取第百分比 * @param sortList * @param percent * @return */ private static long computeExecTimePercent(List<Long> sortList,double percent){ return sortList.get((int)Math.ceil(percent*sortList.size())); } /** * 根据总量取随机数(百分比)的平均值 * @param sortList * @param percent * @return */ private static long computeExecTimePercentByrand(List<Long> sortList,double percent){ int totalSize = sortList.size(); int samples = (int)Math.ceil(percent*totalSize); List<Long> exeTimeList = Lists.newArrayListWithCapacity(samples); for (int j=0;j<samples;j++){ exeTimeList.add(sortList.get(RandomUtil.randomInt(totalSize))); } return (long)exeTimeList.stream().mapToLong((x)->x).average().orElse(0); } /** * * @param url * @param reqtotalCount * @param concuurent * @return */ private static ExecRet exec(String url,int reqtotalCount,int concuurent) { // 1、资源准备,线程池和http链接池等 BlockingQueue<Runnable> workQueue= new LinkedBlockingQueue<>(reqtotalCount); ThreadPoolExecutor threadPoolExecutor = new MyPool(concuurent,concuurent,20, TimeUnit.SECONDS,workQueue); threadPoolExecutor.prestartAllCoreThreads(); ConnectionProfile connectionProfile = ConnectionProfile.builder().maxConnections(10).build(); //connectionProfile.setShortConn(true); 是否为短连接 SimpleHttpClientFacade httpClientFacade = new SimpleHttpClientFacade(connectionProfile); // 2、task 任务提交 CountDownLatch countDownLatch = new CountDownLatch(reqtotalCount); for (int i=0;i< reqtotalCount;i++){ Task task = new Task(httpClientFacade,url,countDownLatch); threadPoolExecutor.submit(task); } try { countDownLatch.await(); } catch (InterruptedException e) { } //3、获取执行结果 List<Long> exeTimeList = ((MyPool)threadPoolExecutor).getExecTimeList(); if (exeTimeList.size()!=0){ Collections.sort(exeTimeList); System.out.println("执行数量"+exeTimeList.size()+":"+exeTimeList); } double average = exeTimeList.stream().mapToLong((x)->x).average().orElse(0); ExecRet exeRet = new ExecRet(); exeRet.setAvgExtime(average); exeRet.setExecTimePercent(computeExecTimePercentByrand(exeTimeList,0.95)); return exeRet; } public static void main(String[] args) throws IOException, URISyntaxException { ExecRet exeRet = exec("https://www.baidu.com",100,10); System.out.println("平均响应时间(ms):"+exeRet.getAvgExtime()+",95%响应时间(ms):"+exeRet.getExecTimePercent()); }}
一些为几个辅助实体类:
线程池,继承ThreadPoolExecutor,重写方法实现 每次运行的执行时间
public class MyPool extends ThreadPoolExecutor { public MyPool(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); this.startTimes = new ConcurrentHashMap<>(); } private ConcurrentHashMap<String, Long> startTimes; @Getter private List<Long> execTimeList = new LinkedList<>(); @Override protected void beforeExecute(Thread t, Runnable r) { startTimes.put(String.valueOf(r.hashCode()), System.currentTimeMillis()); } @Override protected void afterExecute(Runnable r, Throwable t) { long startDate = startTimes.remove(String.valueOf(r.hashCode())); long diff = System.currentTimeMillis() - startDate; execTimeList.add(diff); }
执行结果类
@Datapublic class ExecRet { private double avgExtime; private long execTimePercent;}
任务类,具体执行的任务
@Datapublic class Task implements Runnable{ private SimpleHttpClientFacade httpClientFacade; private String url; private CountDownLatch countDownLatch; public Task(SimpleHttpClientFacade httpClientFacade,String url,CountDownLatch countDownLatch){ this.httpClientFacade = httpClientFacade; this.url = url; this.countDownLatch = countDownLatch; } @Override public void run() { try { httpClientFacade.httpGet(url); } catch (Exception e) { System.out.println("========执行失败"); } countDownLatch.countDown(); }}
执行结果:
执行数量100:[25, 27, 27, 28, 28, 28, 29, 29, 30, 30, 30, 31, 31, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 36, 36, 36, 36, 36, 36, 36, 37, 37, 37, 37, 38, 38, 39, 39, 39, 40, 40, 41, 41, 41, 42, 42, 42, 43, 43, 44, 44, 44, 45, 45, 45, 45, 45, 46, 48, 49, 50, 50, 51, 52, 52, 54, 54, 55, 57, 57, 61, 62, 63, 65, 68, 76, 80, 201, 506, 506, 507, 507, 508, 509, 511, 513, 514, 517]平均响应时间(ms):89.21,95%响应时间(ms):80
划线
评论
复制
发布于: 2020 年 07 月 21 日阅读数: 61
ashuai1106
关注
还未添加个人签名 2017.10.20 加入
还未添加个人简介
评论