写点什么

7.7 第七周课后练习

用户头像
张荣召
关注
发布于: 2020 年 11 月 08 日

1.性能压测时,随着并发压力的增加,系统响应时间和吞吐量如何变化,为什么?

解析:性能压测,随着并发压力的增加,系统大概经历三个阶段,系统响应时间和吞吐量,再每个阶段有不同变化。

阶段 1:性能测试阶段:以系统设计初期规划的性能指标为预期指标,对系统不断施加压力,验证系统在资源可接受的范围内,是否达到性能预期。

主表表现:随着并发数增加,响应时间稍微增加,吞吐量线性上升,如图 1(a-b 阶段)。

主要原因:在该阶段,系统资源充足(CPU,内存,磁盘,网络等资源),

增加的并发请求,被系统处理时,能够分配到足够的资源。

没有发生系统资源争抢,请求不会因为资源不足而排队。吞吐量呈现线性上升趋势。

阶段 2:负载测试阶段:对系统不断增加并发请求以增加系统压力,直到系统某一项或者多项性能指标达到安全临界值。

主要表现:随着并发数继续增加,系统吞吐量越过 b 点,呈现抛物线状达到顶点 C 最大吞吐量。

响应时间显著增长,但是错误率较低,再可接受范围内,如图 1(b-c 阶段)。

主要原因: 在该阶段,系统性能指标一项或者多项指标达到安全临界值,也就是说:系统资源一项或者多项已经消耗完(比如:CPU 使用率刚好达到 100%,或者内存使用率刚好达到 100%,或者网络带宽数据传输量刚好处于峰值状态等等),此时并发数是系统可处理的最高并发数,吞吐量是系统最高吞吐量。

阶段 3:压力测试阶段:超过安全负载的情况下,继续增加并发数,对系统施加压力,直到系统崩溃不能处理任何请求,以此来获得系统最大压力承受能力。

主要表现:超过安全负载 C 点后,系统响应时间缓慢,甚至超时,错误率也明显增长。吞吐量呈现下降趋势,直到系统不能处理请求而崩溃。如图 1(c-d)阶段。

主要原因:超过安全负载点 C 后,继续增加并发数,新增加的并发请求,分配不到足够的系统资源(比如:CPU 已经 100%满负荷运行,无法将更多的线程调度到 CPU 上运行;内存已经使用 100%,无法创建更多的线程服务增加的并发请求;网络传输长时间处于峰值状态),新增加的请求因为资源不足而等待,不能得到快速处理,长时间等待资源,甚至超时无响应,并发数到达某一点后,系统无法处理请求而崩溃。



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

解析:

/** * 测试工具 * */public interface Tester {    void test();}
复制代码


package org.geekbang.training.arch;
import java.net.URI;import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.time.Duration;import java.util.concurrent.*;
/** * Web性能测试 * */public class WebPerformanceTester implements Tester { private ExecutorService executors = null; private HttpRequest[] requests = null; private Future<Long>[] results = null; private final HttpClient httpClient = HttpClient.newBuilder() .version(HttpClient.Version.HTTP_1_1) .connectTimeout(Duration.ofSeconds(10)) .build();
public WebPerformanceTester(String url, int requestCount, int concurrencyCount){ executors = Executors.newFixedThreadPool(concurrencyCount); requests = new HttpRequest[requestCount]; results = new Future[requestCount]; for(int i=0;i<requestCount;i++){ requests[i] = HttpRequest.newBuilder() .GET() .uri(URI.create(url)) .setHeader("User-Agent", "Java 11 HttpClient Bot") // add request header .build(); } }
public void test() { /*启动多线程,并行发出请求**/ for(int i=0;i<requests.length;i++){ Callable<Long> task = new HttpTask(httpClient,requests[i],i); results[i] = executors.submit(task); } executors.shutdown(); System.out.println("平均请求时间(毫秒);"+TimeUnit.NANOSECONDS.toMillis(CalculateAvgResponseTime())); System.out.println("95%平均请求时间(毫秒);"+TimeUnit.NANOSECONDS.toMillis(CalculateAvgResponseTimeByPercent(95))); } private Long CalculateAvgResponseTime(){ Long sum=0L; for(Future<Long> result:results){ try { sum+=result.get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } return sum/(results.length); }
private Long CalculateAvgResponseTimeByPercent(int number){ Long[] resultArray = new Long[results.length]; for(int i=0;i<resultArray.length;i++){ try { resultArray[i] = results[i].get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } }
/***排序*/ for(int i=0;i<resultArray.length;i++){ for(int j=0;j<resultArray.length-i-1;j++){ if(resultArray[j]>resultArray[j+1]){ Long temp=resultArray[j]; resultArray[j]=resultArray[j+1]; resultArray[j+1]=temp; } } } Long sum=0L; double ceilByPercent = Math.ceil(resultArray.length*(number/100.0)); for(int i=0;i<ceilByPercent;i++){ sum+=resultArray[i]; } return (long)(sum/ceilByPercent); }}
复制代码


package org.geekbang.training.arch;
import java.net.http.HttpClient;import java.net.http.HttpRequest;import java.net.http.HttpResponse;import java.util.concurrent.Callable;import java.util.concurrent.CompletableFuture;import java.util.concurrent.TimeUnit;
public class HttpTask implements Callable<Long> { private HttpClient httpClient; private HttpRequest request; private int index; public HttpTask(HttpClient client,HttpRequest request,int index){ this.httpClient=client; this.request=request; this.index=index; }
@Override public Long call() throws Exception { try { long start = System.nanoTime(); //System.out.println(index+"请求开始时间:" + start); CompletableFuture<HttpResponse<String>> response = httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString()); String result = response.thenApply(HttpResponse::body).get(); long end = System.nanoTime(); //System.out.println(index+"请求结束时间:" + end); System.out.println("线程"+Thread.currentThread().getId()+"-第"+index + "个请求响应时间(毫秒):" + TimeUnit.NANOSECONDS.toMillis(end-start)); return end-start; } catch (Exception e) { e.printStackTrace(); } return 0L; }}
复制代码


package org.geekbang.training.arch;
import java.io.IOException;
public class WebProfiler { public static void main(String[] args)throws IOException { Tester tester = new WebPerformanceTester("https://www.baidu.com",100,10); tester.test(); System.in.read(); }}
复制代码

输出结果:

线程 20-第 6 个请求响应时间(毫秒):904

线程 23-第 9 个请求响应时间(毫秒):913

线程 21-第 7 个请求响应时间(毫秒):924

线程 22-第 8 个请求响应时间(毫秒):924

线程 15-第 1 个请求响应时间(毫秒):911

线程 17-第 3 个请求响应时间(毫秒):914

线程 18-第 4 个请求响应时间(毫秒):912

线程 19-第 5 个请求响应时间(毫秒):922

线程 16-第 2 个请求响应时间(毫秒):904

线程 14-第 0 个请求响应时间(毫秒):923

线程 20-第 10 个请求响应时间(毫秒):33

线程 16-第 18 个请求响应时间(毫秒):24

线程 23-第 11 个请求响应时间(毫秒):36

线程 18-第 16 个请求响应时间(毫秒):36

线程 21-第 12 个请求响应时间(毫秒):43

线程 14-第 19 个请求响应时间(毫秒):32

线程 22-第 13 个请求响应时间(毫秒):44

线程 17-第 15 个请求响应时间(毫秒):46

线程 15-第 14 个请求响应时间(毫秒):47

线程 19-第 17 个请求响应时间(毫秒):48

线程 17-第 27 个请求响应时间(毫秒):15

线程 18-第 23 个请求响应时间(毫秒):26

线程 15-第 28 个请求响应时间(毫秒):21

线程 21-第 24 个请求响应时间(毫秒):28

线程 23-第 22 个请求响应时间(毫秒):34

线程 16-第 21 个请求响应时间(毫秒):38

线程 14-第 25 个请求响应时间(毫秒):27

线程 22-第 26 个请求响应时间(毫秒):27

线程 20-第 20 个请求响应时间(毫秒):46

线程 19-第 29 个请求响应时间(毫秒):25

线程 18-第 31 个请求响应时间(毫秒):20

线程 17-第 30 个请求响应时间(毫秒):20

线程 21-第 33 个请求响应时间(毫秒):47

线程 16-第 35 个请求响应时间(毫秒):46

线程 20-第 38 个请求响应时间(毫秒):39

线程 23-第 34 个请求响应时间(毫秒):52

线程 15-第 32 个请求响应时间(毫秒):56

线程 22-第 37 个请求响应时间(毫秒):53

线程 19-第 39 个请求响应时间(毫秒):50

线程 18-第 40 个请求响应时间(毫秒):45

线程 14-第 36 个请求响应时间(毫秒):62

线程 21-第 42 个请求响应时间(毫秒):19

线程 17-第 41 个请求响应时间(毫秒):52

线程 20-第 44 个请求响应时间(毫秒):29

线程 16-第 43 个请求响应时间(毫秒):30

线程 23-第 45 个请求响应时间(毫秒):25

线程 15-第 46 个请求响应时间(毫秒):52

线程 20-第 53 个请求响应时间(毫秒):34

线程 16-第 54 个请求响应时间(毫秒):35

线程 22-第 47 个请求响应时间(毫秒):54

线程 17-第 52 个请求响应时间(毫秒):47

线程 21-第 51 个请求响应时间(毫秒):48

线程 14-第 50 个请求响应时间(毫秒):53

线程 18-第 49 个请求响应时间(毫秒):59

线程 23-第 55 个请求响应时间(毫秒):41

线程 19-第 48 个请求响应时间(毫秒):61

线程 20-第 57 个请求响应时间(毫秒):34

线程 15-第 56 个请求响应时间(毫秒):38

线程 14-第 62 个请求响应时间(毫秒):30

线程 22-第 59 个请求响应时间(毫秒):35

线程 21-第 61 个请求响应时间(毫秒):35

线程 16-第 58 个请求响应时间(毫秒):37

线程 17-第 60 个请求响应时间(毫秒):31

线程 19-第 65 个请求响应时间(毫秒):34

线程 23-第 64 个请求响应时间(毫秒):41

线程 18-第 63 个请求响应时间(毫秒):42

线程 20-第 66 个请求响应时间(毫秒):20

线程 21-第 70 个请求响应时间(毫秒):55

线程 22-第 69 个请求响应时间(毫秒):56

线程 19-第 73 个请求响应时间(毫秒):50

线程 15-第 67 个请求响应时间(毫秒):59

线程 16-第 71 个请求响应时间(毫秒):56

线程 14-第 68 个请求响应时间(毫秒):58

线程 20-第 76 个请求响应时间(毫秒):41

线程 23-第 74 个请求响应时间(毫秒):46

线程 18-第 75 个请求响应时间(毫秒):46

线程 17-第 72 个请求响应时间(毫秒):56

线程 18-第 85 个请求响应时间(毫秒):16

线程 22-第 78 个请求响应时间(毫秒):19

线程 15-第 80 个请求响应时间(毫秒):18

线程 19-第 79 个请求响应时间(毫秒):19

线程 21-第 77 个请求响应时间(毫秒):20

线程 14-第 82 个请求响应时间(毫秒):19

线程 17-第 86 个请求响应时间(毫秒):18

线程 16-第 81 个请求响应时间(毫秒):19

线程 23-第 84 个请求响应时间(毫秒):22

线程 20-第 83 个请求响应时间(毫秒):23

线程 15-第 89 个请求响应时间(毫秒):13

线程 19-第 90 个请求响应时间(毫秒):13

线程 18-第 87 个请求响应时间(毫秒):14

线程 17-第 93 个请求响应时间(毫秒):13

线程 22-第 88 个请求响应时间(毫秒):13

线程 21-第 91 个请求响应时间(毫秒):14

线程 16-第 94 个请求响应时间(毫秒):13

线程 23-第 95 个请求响应时间(毫秒):11

线程 14-第 92 个请求响应时间(毫秒):14

线程 20-第 96 个请求响应时间(毫秒):14

线程 18-第 99 个请求响应时间(毫秒):9

线程 19-第 98 个请求响应时间(毫秒):16

线程 15-第 97 个请求响应时间(毫秒):16

平均请求时间(毫秒);122

95%平均请求时间(毫秒);80


用户头像

张荣召

关注

还未添加个人签名 2018.05.02 加入

还未添加个人简介

评论

发布
暂无评论
7.7 第七周课后练习