架构师训练营 Week7 - 课后作业
并发数,响应时间,吞吐量
性能压测的时候,随着并发压力的增加,系统响应时间和吞吐量如何变化,为什么?
1,概念:
响应时间:系统从收到请求到响应发出所占用的时间
并发数:系统能同时处理的请求数量
并发请求数
在线用户数
系统用户数
吞吐量:单位时间内系统处理的请求数量。
QPS: 每秒查询数量
TPS: 每秒交易数量
HPS:每秒HTTP数量
2,关系:
整个系统从单机(垂直伸缩), 集群(水平伸缩)方面资源是有限的。同时站在成本收益比的角度,资源分配和最大可处理的请求达到一个比较好的平衡(理想情况下)。 借用课上的几张图:
并发数 VS 吞吐量:
初始阶段线性增加(a->b),系统资源充足。 随着并发数的增加吞吐量增加,系统资源逐步被占满。(a->b)
吞吐量增加变慢:请求数量增加,系统需要分配一部分资源处理调度,等待资源释放。吞吐量增加速度减慢。
临界点(c),当并发数达到某个数量级时,系统资源(CPU,数据库连接,JVM堆内存/GC...)被充分占用,不能同时响应新的请求。此时就会出现请求等待排队。
超过临界点(c->d), 当有更多的请求堆积等待,系统需要额外分出一部分资源去处理额外的等待请求。对于单机处理会出现频繁的线程切换,Full GC.这些处理都会有消耗,导致吞吐量下降。
出错/崩溃:当请求数量大大超出系统处理能力,就会出现请求超时。系统内部资源处理不当会导致OOM (GC失败)或者系统down机。

并发数VS响应时间yu
与吞吐量对应。
初始状态:并发请求都可以分配到足够的系统资源处理,响应时间基本一致
响应时间增加:随着资源被占用,更多的请求不能立马被处理,平均响应市场增加。
临界点:当达到某个临界点,系统为处理额外的消耗占用的资源时间增加。单个请求等待的时常幅度增大
出错/崩溃:达到某个请求数量系统开始崩溃,响应时间达到最大值。系统超时,宕机。

Web 性能压测工具 - LoadRunner
1. 执行
> java -jar .\HttpLoadRunner-1.0-SNAPSHOT.jar http://www.baidu.com c10 t100
Testing Url: http://www.baidu.com
Concurrent: 10, Total Num: 100
================= Load Runner Result / Million Seconds ==================
Executed Num: 100, Failure Num: 0
Avg Time: 63.21, 95% Avg Time: 52.53, 95% Index: 95
Total Duration: 6401, Sum of TimeSpent:6321
> java -jar .\HttpLoadRunner-1.0-SNAPSHOT.jar http://www.baidu.com c100 t100
Testing Url: http://www.baidu.com
Concurrent: 100, Total Num: 100
================= Load Runner Result / Million Seconds ==================
Executed Num: 100, Failure Num: 0
Avg Time: 58.74, 95% Avg Time: 48.38, 95% Index: 95
Total Duration: 5994, Sum of TimeSpent:5874
> java -jar .\HttpLoadRunner-1.0-SNAPSHOT.jar http://www.baidu.com c100 t1000
Testing Url: http://www.baidu.com
Concurrent: 100, Total Num: 1000
================= Load Runner Result / Million Seconds ==================
Executed Num: 1000, Failure Num: 0
Avg Time: 48.64, 95% Avg Time: 45.74, 95% Index: 950
Total Duration: 49015, Sum of TimeSpent:48644
2. 源代码
package io.http;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import java.util.Arrays;
import java.util.concurrent.*;
public class LoadRunner {
private static int concurrentNum = 10;
private static int totalNum = 100;
private static String url = "http://www.baidu.com";
public static void main(String[] args) throws Exception{
//init member variable;
init(args);
System.out.printf("Testing Url: %s\n", url);
System.out.printf("Concurrent: %3d, Total Num: % d\n", concurrentNum, totalNum);
long startTime = System.currentTimeMillis();
int failureNum = 0;
long totalTime = 0;
long sumOfTimeSpent = 0;
double totalAvg = 0;
double percAvg = 0;
long[] durations = new long[totalNum];
ExecutorService executor = Executors.newFixedThreadPool(concurrentNum);
try{
for (int i = 0; i < totalNum; i++){
FutureTask requestTask = getLongFutureTask(i);
executor.submit(requestTask);
Long timeSpent = requestTask.get(30, TimeUnit.SECONDS);
if (timeSpent != -1) {
durations[i] = timeSpent;
sumOfTimeSpent += timeSpent;
} else {
durations[i] = Long.MAX_VALUE;
failureNum++;
}
}
Arrays.sort(durations);
totalAvg = Arrays.stream(durations).average().orElse(Double.NaN);
int percIndex = Math.round((totalNum-failureNum) * 95/100);
percAvg = Arrays.stream( Arrays.copyOfRange(durations, 0, percIndex)).average().orElse(Double.NaN);
totalTime = System.currentTimeMillis() - startTime;
System.out.println("================= Load Runner Result / Million Seconds ==================");
System.out.printf("Executed Num: %d, Failure Num: %d\n", totalNum, failureNum);
System.out.printf("Avg Time: %.2f, 95%% Avg Time: %.2f, 95%% Index: %d\n", totalAvg, percAvg, percIndex);
System.out.printf("Total Duration: %d, Sum of TimeSpent:%d\n", totalTime, sumOfTimeSpent);
} finally {
executor.shutdown();
}
}
private static FutureTask getLongFutureTask(int i) {
FutureTask requestTask = new FutureTask(new Callable() {
public Long call() throws Exception {
Long start = System.currentTimeMillis();
Long duration = -1L;
//HTTP client call
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpGet httpGet = new HttpGet(url);
CloseableHttpResponse response = httpclient.execute(httpGet);
try {
HttpEntity responseEntity = response.getEntity();
if(HttpStatus.SC_OK == response.getStatusLine().getStatusCode()) {
duration = System.currentTimeMillis() - start;
} else {
// other case consider as failed.
duration = -1L;
}
// System.out.printf("Thread: %s, Duration: %s\n", Thread.currentThread().getName(), duration);
} finally {
response.close();
}
return duration;
}
}) ;
return requestTask;
}
static void init(String[] args) {
if(args == null) return;
for (String arg : args) {
if (arg == null || arg.length() <= 1) continue;
//input url
if (arg.startsWith("http://")) {
url = arg;
continue;
}
String paramName = arg.substring(0,1);
String paramValue = arg.substring(1);
//input concurrent number
if (paramName.equalsIgnoreCase("c") && paramValue.matches("[0-9]+")){
concurrentNum = Integer.parseInt(arg.substring(1));
continue;
}
//input duration seconds
if (paramName.equalsIgnoreCase("t") && paramValue.matches("[0-9]+")){
totalNum = Integer.parseInt(arg.substring(1));
continue;
}
}
}
}