架构师训练营作业 -- Week 7

发布于: 2020 年 07 月 20 日
架构师训练营作业 -- Week 7

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

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

性能测试曲线

性能测试曲线

由上图可见,性能测试可分成三个阶段,既轻载区重载区危险(拐点)区。我们假设轻载区的上边界对应的并发数为L, 重载区的上边界对应的并发数为H。当并发数低于L时,吞吐量(紫色)随并发数增长曾现线性增长。也就是说,当并发数小于L时,系统的性能在正常范围,可以很好地应对吞吐压力。当并发数大于L小于H时,对应系统性能曲线的重载区。此时可能系统的CPU,内存,或者网络等资源的一项或者多项的使用率达到了极限,已经有一部分用户需要等待更长时间获得响应。可以看到响应时间曲线(蓝色)斜率增加,而吞吐量也逐渐达到极限甚至开始下降。当并发数超过H,来到危险区时,所有系统资源都已经耗尽,可能已经引发系统阻塞,吞吐量迅速下降,而响应时间也呈指数级增加,系统性能严重下降,濒临崩溃。

性能测试的Java实现

Java实现如下:

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Scanner;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Dispatcher;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class PerformanceTest {
private static String targetUrl = "http://www.baidu.com";
private static int concurrentRequests = 100;
private static int repeatTimes = 3;
private static CountDownLatch batchLatch;
private static List<String> statistics = Collections.synchronizedList(new ArrayList<>());
public static void main(String[] args) throws InterruptedException {
// set arguments
setArguments();
OkHttpClient httpClient = buildHttpClient();
// do performance test
for (int i = 0; i < repeatTimes; i++) {
batchLatch = new CountDownLatch(concurrentRequests);
doPerformanceTesting(httpClient, i);
batchLatch.await(5, TimeUnit.SECONDS);
}
// generate report
generateReport();
System.exit(1);
}
private static void generateReport() {
List<String[]> list = statistics.stream()
.map(s -> s.split(","))
.collect(Collectors.toList());
Collections.sort(list, new Comparator<String[]>() {
@Override
public int compare(String[] o1, String[] o2) {
if (o1 == null || o2 == null || o1.length < 4 || o2.length < 4) {
return -1;
}
return Integer.valueOf(o1[2]) - Integer.valueOf(o2[2]);
}
});
double avg = list.stream()
.collect(Collectors.summarizingInt(e -> Integer.valueOf(e[2])))
.getAverage();
System.out.println("Average response time: " + avg + "ms");
System.out.println("95th percentile response time: " + list.get(
(int)Math.floor(list.size() * .95f)
)[2]
+ "ms");
long failureCnt = list.stream().filter(e -> "False".equals(e[3])).count();
System.out.println("Failure rate: " + (failureCnt / list.size() * 100) + "%");
}
private static void doPerformanceTesting(OkHttpClient httpClient, int batchNumber) {
Request req = new Request.Builder().get().url(targetUrl).build();
for (int i = 0; i < concurrentRequests; i++) {
httpClient.newCall(req).enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) throws IOException {
try {
batchLatch.countDown();
String stat = String.format("%d,%s,%d,%s",
batchNumber,
Thread.currentThread().getId(),
response.receivedResponseAtMillis() - response.sentRequestAtMillis(),
"True");
System.out.println(stat);
statistics.add(stat);
} finally {
response.close();
}
}
@Override
public void onFailure(Call call, IOException e) {
batchLatch.countDown();
String stat = String.format("%d,%s,%d,%s",
batchNumber,
Thread.currentThread().getName(),
-1,
"False");
System.out.println(stat);
statistics.add(stat);
}
});
}
}
private static OkHttpClient buildHttpClient() {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
ExecutorService exec = new ThreadPoolExecutor(concurrentRequests, concurrentRequests, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
Dispatcher d = new Dispatcher(exec);
builder.dispatcher(d);
OkHttpClient httpClient = builder.build();
httpClient.dispatcher().setMaxRequestsPerHost(concurrentRequests);
return httpClient;
}
private static void setArguments() {
// set target url
System.out.println("Please input target URL (http://www.baidu.com by default): ");
Scanner scanner = new Scanner(System.in);
String url = scanner.nextLine();
if (StringUtils.isNotBlank(url)) {
targetUrl = url;
}
System.out.println("Target URL: " + targetUrl);
System.out.println();
// set number of concurrent requests
System.out.println("Please input number of concurrent requests: ");
int cnt = scanner.nextInt();
if (cnt > 0) {
concurrentRequests = cnt;
}
System.out.println("Number of concurrent requests: " + concurrentRequests);
System.out.println();
// set repeat times
System.out.println("Please input repeat times: ");
int times = scanner.nextInt();
if (times > 0) {
repeatTimes = times;
}
System.out.println("Repeat times: " + repeatTimes);
System.out.println();
}
}

输出结果:

Please input target URL (http://www.baidu.com by default):
Target URL: http://www.baidu.com
Please input number of concurrent requests:
10
Number of concurrent requests: 10
Please input repeat times:
100
Repeat times: 100
0,13,57,True
0,18,59,True
...
99,22,31,True
Average response time: 38.991ms
95th percentile response time: 37ms
Failure rate: 0

发布于: 2020 年 07 月 20 日 阅读数: 27
用户头像

吴炳华

关注

还未添加个人签名 2020.04.08 加入

还未添加个人简介

评论

发布
暂无评论
架构师训练营作业 -- Week 7