【第七周】命题作业——性能压测工具

用户头像
三尾鱼
关注
发布于: 2020 年 07 月 19 日

以下两题,至少选做一题

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

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



答:

1、并发增加时响应时间和吞吐量变化

第一阶段:系统资源利用率都在指标范围内

响应时间基本保持稳定,吞吐量逐步放大。

第二阶段:系统资源利用率超过指标,逐步到达瓶颈(最大值)

响应时间会有所延长,吞吐量继续放大,但放大的速度逐渐减缓。

第三阶段:系统资源超过瓶颈

响应时间快速延长,甚至超时,吞吐量迅速降低。

2、Web性能压测工具



1、压测核心工具类

提供压力并发和计数统计等功能

package xyz.hs.geek.training.week7;
import java.util.concurrent.*;
/**
* @author huangsui
* Created on 2020/7/20
*/
public class StressTestingUtils {
public static StressResult testUrl(int totalRequest, int concurrencyLevel, String url) {
return test(totalRequest, concurrencyLevel, new HttpTask(url));
}
public static StressResult test(int totalRequest, int concurrencyLevel, Task task) {
StressResult result = new StressResult(totalRequest, concurrencyLevel);
long start = System.currentTimeMillis();
// ExecutorService executorService = Executors.newFixedThreadPool(nThreads);
ExecutorService executorService = new ThreadPoolExecutor(concurrencyLevel, concurrencyLevel, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(totalRequest));
Semaphore semaphore = new Semaphore(concurrencyLevel);// 控制最大并发数
CountDownLatch countDownLatch = new CountDownLatch(totalRequest);// 并发计数等待
CyclicBarrier cyclicBarrier = new CyclicBarrier(concurrencyLevel);
for (int i = 0; i < totalRequest; i++) {
executorService.execute(() -> {
try {
semaphore.acquire();// 控制最大并发数
try {
// cyclicBarrier.await();// 集合完成后才出发
long startOfReq = System.currentTimeMillis();
boolean success = task.run();
long endOfReq = System.currentTimeMillis();
if(success){
result.complete(1);
result.cost(endOfReq - startOfReq);
}else {
result.fail(1);
}
} catch (Exception e) {
e.printStackTrace();
result.fail(1);
} finally {
countDownLatch.countDown();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
});
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
executorService.shutdown();
long end = System.currentTimeMillis();
result.setTotalTime(end - start);
return result.stats();
}
}

这里线程池和Semaphore都提供了最大并发数的控制。



2、压测结果类

package xyz.hs.geek.training.week7;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author huangsui
* Created on 2020/7/21
*/
@Getter
@Setter
@ToString
public class StressResult {
private final int totalRequest;
private final int concurrencyLevel;
private final List<Long> reqTimeList;
private long totalCost; // 请求花费时间总和
private long totalTime; // 测试时间
private AtomicInteger completed = new AtomicInteger(0);
private AtomicInteger failed = new AtomicInteger(0);
private double qps;/*Requests per second*/
private double avgTime;
private long shortestTime;
private long fiftyTime;
private long ninetyFiveTime;
private long hundredTime;
public StressResult(int totalRequest, int concurrencyLevel) {
this.totalRequest = totalRequest;
this.concurrencyLevel = concurrencyLevel;
this.reqTimeList = new ArrayList<>(totalRequest);
totalCost = 0L;
}
public void complete(int num){
completed.addAndGet(num);
}
public void cost(long cost){
reqTimeList.add(cost);
totalCost += cost;
}
public void fail(int num){
failed.addAndGet(num);
}
public StressResult stats(){
this.avgTime = totalCost/completed.get();
this.qps = totalRequest/totalCost;
reqTimeList.sort(Comparator.comparingLong(Long::longValue));
ninetyFiveTime = reqTimeList.get(Double.valueOf(reqTimeList.size()*0.95).intValue());
return this;
}
}



3、任务类

定义和实现具体任务

package xyz.hs.geek.training.week7;
/**
* @author huangsui
* Created on 2020/7/21
*/
public interface Task {
boolean run() throws Exception;
}



package xyz.hs.geek.training.week7;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;
import java.util.concurrent.BrokenBarrierException;
/**
* @author huangsui
* Created on 2020/7/21
*/
public class HttpTask implements Task {
private final static Logger logger = LoggerFactory.getLogger(HttpTask.class);
private String url;
public HttpTask(String url) {
this.url = url;
}
@Override
public boolean run() throws URISyntaxException, BrokenBarrierException, InterruptedException {
return doRequest(createHttpGet(url, null));
}
private HttpUriRequest createHttpGet(String url, Map<String, String> param) throws URISyntaxException {
URIBuilder builder = new URIBuilder(url);
if (param != null) {
for (String key : param.keySet()) {
builder.addParameter(key, param.get(key));
}
}
URI uri = builder.build();
return new HttpGet(uri);
}
private HttpUriRequest createHttpPost(String url, String json){
HttpPost httpPost = new HttpPost(url);
httpPost.setEntity(new StringEntity(json, ContentType.APPLICATION_JSON));
return httpPost;
}
public boolean doRequest(HttpUriRequest httpUriRequest) {
boolean result = true;
try(CloseableHttpClient httpClient = HttpClients.createDefault()) {
// logger.info("发起请求:{}", httpUriRequest.getURI());
try (CloseableHttpResponse response = httpClient.execute(httpUriRequest)){
if(response.getStatusLine().getStatusCode() != 200){
result = false;
}
// String respStr = EntityUtils.toString(response.getEntity(), "UTF-8");
// logger.info("收到响应:{},{}",response.getStatusLine().getStatusCode(), respStr);
}
} catch (Exception e) {
logger.error("请求异常:{}", e.toString());
result = false;
}
return result;
}
}



4、进行压测

package xyz.hs.geek.training;
import org.junit.Test;
import xyz.hs.geek.training.week7.StressResult;
import xyz.hs.geek.training.week7.StressTestingUtils;
/**
* @author huangsui
* Created on 2020/7/21
*/
public class StressTestingUtilsTests {
@Test
public void test(){
StressResult result = StressTestingUtils.testUrl(100, 10, "http://www.baidu.com");
// System.out.println(result);
System.out.printf("总请求数:%d \n", 100);
System.out.printf("并发数:%d \n", 10);
System.out.printf("平均响应时间:%f ms\n", result.getAvgTime());
System.out.printf("95%%响应时间:%d ms\n", result.getNinetyFiveTime());
}
}

压测输出结果:

平均响应时间:372ms,95%响应时间:2920ms



用户头像

三尾鱼

关注

还未添加个人签名 2018.07.10 加入

还未添加个人简介

评论

发布
暂无评论
【第七周】命题作业——性能压测工具