写点什么

架构师训练营第 1 期 - 第 7 周课后练习

用户头像
Anyou Liu
关注
发布于: 2020 年 11 月 08 日
  1. 性能压测的时候,随着并发压力的增加,系统响应时间和吞吐量如何变化,为什么?

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

答:


响应时间是指系统发出请求到最后接收到响应数据所花的时间。吞吐量是指单位时间内系统能够处理的请求的数量,比如 TPS(每秒事务数)、QPS(每秒查询数)。吞吐量 =(1000/响应时间 ms)× 并发数

在性能压测的时候,随着并发数的增加,响应时间和并发数会有如下曲线:

  • 在系统最佳运行点之前,并发数比较少,系统资源充足,可以快速处理用户的请求,响应时间比较低。

  • 在系统最大负载之前,随着并发数的增加,系统资源不足,用户请求处理不及时,后面的请求需要等待前面的请求处理完才能获得响应,有一定的延迟,响应时间随之增加。

  • 当超过系统最大负载,并发数继续增加,系统资源严重不足,请求处理变得缓慢,cpu 处理请求线程不及时,大量请求等待,导致响应时间持续增大,当到达系统崩溃点时,系统无法响应用户的请求,系统直接瘫痪了。

吞吐量和并发数的曲线如下:

  • 在系统最佳运行点之前,并发数比较低,系统处理速度快,单位时间内能够处理的请求多。

  • 随着并发数量的增加,虽然系统处理速度变慢,但是单位时间内能够处理的请求数量还是持续增加的,在系统最大负载点之前,达到最大的吞吐量。

  • 在系统最大负载点之后,继续增加并发数的话,系统处理速度变得非常缓慢,单位时间内能够处理的请求数量相较于最大负载点是下降的,如果继续增加并发数,系统会资源耗尽导致无法响应请求,系统在到达崩溃点之后直接宕机了。

2.

性能测试工具可以设置并发数,并发数就是线程数,可以启动一个线程池来执行请求,线程池的大小就是并发的大小。PerfTool 的代码如下:

  • 构造方法需要传入线程池的大小,并初始化线程池,实现了自定义的 ThreadFactory

  • run 方法接收参数 url 和请求次数,方法是异步执行的,会返回一个 CompletableFuture。supplyAsync 方法里面引入了 HttpClient 来发送请求,每次发送都统计时间,并收集到 responseTimeList 中,最后返回 PerfResult 来计算统计结果

package perftool;
import org.apache.http.HttpStatus;import org.apache.http.client.methods.HttpGet;import org.apache.http.client.methods.HttpUriRequest;import org.apache.http.impl.client.HttpClients;
import java.util.List;import java.util.concurrent.*;
public class PerfTool {
private final ThreadPoolExecutor executor;
public PerfTool(int numOfThreads) { BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(500); ThreadFactory threadFactory = new PerfThreadFactory(); executor = new ThreadPoolExecutor(numOfThreads, numOfThreads, 30, TimeUnit.SECONDS, workQueue, threadFactory, new ThreadPoolExecutor.AbortPolicy()); }
public CompletableFuture<PerfResult> run(String url, int times) { return CompletableFuture.supplyAsync(() -> { List<Long> responseTimeList = new CopyOnWriteArrayList<>(); CountDownLatch latch = new CountDownLatch(times); int loop = 0;
while (loop < times) { try { executor.submit(() -> { try { HttpUriRequest request = new HttpGet(url); long mills = System.currentTimeMillis(); int code = HttpClients.createDefault().execute(request).getStatusLine().getStatusCode(); assert code == HttpStatus.SC_OK; responseTimeList.add(System.currentTimeMillis() - mills); } catch (Exception e) { System.err.println("Get response failed " + e); } latch.countDown(); }); ++loop; } catch (RejectedExecutionException e) { System.out.println("Rejected task, retry...."); } }
try { latch.await(); } catch (InterruptedException e) { System.err.println("Interrupted..."); } return new PerfResult(responseTimeList); }); }}
复制代码

辅助类 PerfThreadFactory

package perftool;
import java.util.concurrent.ThreadFactory;import java.util.concurrent.atomic.AtomicInteger;
public class PerfThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1); private final ThreadGroup group; private final AtomicInteger threadNumber = new AtomicInteger(1); private final String namePrefix;
public PerfThreadFactory() { SecurityManager sm = System.getSecurityManager(); this.group = sm != null ? sm.getThreadGroup() : Thread.currentThread().getThreadGroup(); this.namePrefix = "perf-pool-" + poolNumber.getAndIncrement() + "-thread-";
}
@Override public Thread newThread(Runnable runnable) { Thread thread = new Thread(this.group, runnable, this.namePrefix + this.threadNumber.getAndIncrement(), 0L); return thread; }}
复制代码

统计结果类 PerfResult

package perftool;
import java.util.Collections;import java.util.List;
public class PerfResult {
private List<Long> responseTimes; private Double avg; private Long _95Percentage;
public PerfResult(List<Long> responseTimes) { this.responseTimes = responseTimes; runStats(); }
private void runStats() { if (responseTimes != null) { long sum = 0l; for (Long time : responseTimes) { sum += time; } double avg = sum * 1d / responseTimes.size(); this.avg = avg; Collections.sort(responseTimes); _95Percentage = responseTimes.get((int)(responseTimes.size() * 0.95) - 1); } }
public List<Long> getResponseTimes() { return Collections.unmodifiableList(responseTimes); }
public Double getAvg() { return avg; }
public Long get_95Percentage() { return _95Percentage; }}
复制代码

单元测试类 PerfToolTest

package perftool;
import org.junit.Test;
import java.util.concurrent.CompletableFuture;
public class PerfToolTest {
@Test public void test10ThreadsAnd100Reqs_GetPerfResult() { CompletableFuture<PerfResult> fut = new PerfTool(10).run("http://www.baidu.com", 100); try { PerfResult result = fut.get(); assert result.getResponseTimes().size() == 100; System.out.println("avg : " + result.getAvg()); System.out.println("95% : " + result.get_95Percentage()); } catch (Exception e) { assert false; } assert true; }}
复制代码

运行结果如下:

avg : 61.2995% : 342
复制代码


用户头像

Anyou Liu

关注

还未添加个人签名 2019.05.24 加入

还未添加个人简介

评论

发布
暂无评论
架构师训练营第 1 期 - 第 7 周课后练习