实现一个简易的压测工具

用户头像
elfkingw
关注
发布于: 2020 年 07 月 22 日
实现一个简易的压测工具

压力测试是模仿多个客户端,并发请求服务器,通过请求的数据和服务监控来评估系统的压力瓶颈,这里用java实现一个简易压力测试工具。主要使用多线程并使用httpClient模拟http请求,把请求结果放在队列中,在从队列中拿到访问结果来分析出平均响应时间时间和90%响应时间。



实现需求:

输入:并发数量、url、和每个并发请求的数量

输出:平均响应时间,10-90%的响应时间

HttpSender接口

public interface HttpSender {

/**
* 发送get请求
* @param url 请求url
* @param paramMap 请求参数
* @return 请求结果
*/
public ResponseResult sendGetRequest(String url, Map paramMap);

/**
* 发送post请求
* @param url 请求url
* @param paramMap 请求参数
* @return 请求结果
*/
public ResponseResult sendPostRequest(String url, Map paramMap);


}


HttpClientSender,使用HttpClient实现http请求发送

public class HttpClientSender implements HttpSender {


public ResponseResult sendGetRequest(String url, Map paramMap) {
ResponseResult responseResult = new ResponseResult();
long start = System.currentTimeMillis();
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
CloseableHttpResponse response = null;
HttpGet httpGet = new HttpGet(getParam(paramMap) != null ? url : url + "?" + getParam(paramMap));
try {
RequestConfig requestConfig = RequestConfig.custom()
// 设置连接超时时间(单位毫秒)
.setConnectTimeout(5000)
// 设置请求超时时间(单位毫秒)
.setConnectionRequestTimeout(5000)
// socket读写超时时间(单位毫秒)
.setSocketTimeout(5000)
// 设置是否允许重定向(默认为true)
.setRedirectsEnabled(true).build();
// 将上面的配置信息 运用到这个Get请求里
httpGet.setConfig(requestConfig);
response = httpClient.execute(httpGet);
responseResult.setResponseStatus(response.getStatusLine().getStatusCode());
HttpEntity responseEntity = response.getEntity();
response.getEntity();
} catch (Exception e) {
e.printStackTrace();
responseResult.setSuccess(false);
} finally {
if (httpClient != null) {
try {
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (response != null) {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
long end = System.currentTimeMillis();
responseResult.setCostTime(new Long(end - start).intValue());
}
return responseResult;
}

private String getParam(Map<String, String> paramMap) {
if (paramMap == null) {
return null;
}
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, String> entry : paramMap.entrySet()) {
String param = entry.getKey();
String paramValue = entry.getKey();
sb.append(param).append("=").append(paramValue).append("&");
}
sb.substring(0, sb.length() - 1);
return sb.toString();
}

private String getContent(InputStream inputStream) {
StringBuilder sb = new StringBuilder();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
String line = null;
try {
while ((line = reader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return sb.toString();
}

public ResponseResult sendPostRequest(String url, Map paramMap) {
return null;
}


}


ResponseResult http返回结果类



public class ResponseResult {

private int costTime;
private String result;
private int responseStatus;
private boolean isSuccess;
private String errorMessage;

public int getCostTime() {
return costTime;
}

public void setCostTime(int costTime) {
this.costTime = costTime;
}

public String getResult() {
return result;
}

public void setResult(String result) {
this.result = result;
}


public boolean isSuccess() {
return isSuccess;
}

public void setSuccess(boolean success) {
isSuccess = success;
}

public String getErrorMessage() {
return errorMessage;
}

public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}

public int getResponseStatus() {
return responseStatus;
}

public void setResponseStatus(int responseStatus) {
this.responseStatus = responseStatus;
}

public String toString() {
return "ResponseResult{" +
"costTime=" + costTime +
", isSuccess=" + isSuccess +
", responseStatus=" + responseStatus +
", errorMessage='" + errorMessage + '\'' +
", result='" + result + '\'' +
'}';
}
}


ResultData 存储返回结果队列类

public class ResultData {
public static Map<String, Queue<ResponseResult>> resultDataMap = new HashMap<>();

public static int count = 0;

private static Queue getResultByKey(String key) {
return resultDataMap.get(key);
}

public synchronized static void addResultByKey(String key, ResponseResult responseResult) {
Queue<ResponseResult> queue = resultDataMap.get(key);
if (queue == null) {
queue = new ConcurrentLinkedQueue<>();
resultDataMap.put(key,queue);
}
queue.add(responseResult);
count++;
}

public static ResponseResult pollResultByKey(String key) {
Queue<ResponseResult> queue = resultDataMap.get(key);
if (queue == null) {
return null;
}
return queue.poll();
}
public static int getSize(String key) {
Queue<ResponseResult> queue = resultDataMap.get(key);
if (queue == null) {
return 0;
}
return queue.size();
}
public static int getCount(String key) {
return count;
}

}


RequestThread 请求线程类

public class RequestThread extends Thread {
private String url;
private Map<String, String> paramMap;
private String batchNo;
private int requestCount;

/**
*
* @param url 请求路劲
* @param paramMap 参数
* @param batchNo 批次号,一次压测的唯一标识
* @param requestCount 单个线程访问次数
*/
public RequestThread(String url, Map<String, String> paramMap, String batchNo, int requestCount) {
this.url = url;
this.paramMap = paramMap;
this.batchNo = batchNo;
this.requestCount = requestCount;
}

public void run() {
HttpSender httpSender = new HttpClientSender();
for (int i = 0; i < requestCount; i++) {
ResponseResult responseResult = httpSender.sendGetRequest(url, paramMap);
ResultData.addResultByKey(batchNo, responseResult);
}
}
}


PessTest 压力测试类

public class PressTest {
/**
* 并发数
*/
private int concurrencyCount;
/**
* 单个并发请求个数
*/
private int requestCount;

public PressTest(int concurrencyCount, int requestCount) {
this.concurrencyCount = concurrencyCount;
this.requestCount = requestCount;
}

public void sendRequest(String url, Map paramMap, String batchNo) {
for (int i = 0; i < concurrencyCount; i++) {
RequestThread requestThread = new RequestThread(url, paramMap, batchNo, requestCount);
requestThread.start();
}
}

public void analyseResult(String batchNo) {
System.out.println("开始=====");
int count = requestCount * concurrencyCount;
List<ResponseResult> resultList = new ArrayList<>();
int i = 0;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(ResultData.getSize(batchNo) + "-" + ResultData.getCount(batchNo));

while (true) {
ResponseResult responseResult = ResultData.pollResultByKey(batchNo);
if (responseResult != null) {
i++;
resultList.add(responseResult);
}
if (i == count - 1) {
break;
}
}
statisticalResult(resultList);
}

private void statisticalResult(List<ResponseResult> responseResults) {
int[] percents = new int[]{10, 20, 30, 40, 50, 60, 70, 80, 90};
int count = responseResults.size();
Collections.sort(responseResults, new Comparator<ResponseResult>() {
public int compare(ResponseResult o1, ResponseResult o2) {
return o1.getCostTime() > o2.getCostTime() ? 1 : -1;
}
});
int sumCost = 0;
for (int i =0;i<count;i++){
ResponseResult responseResult = responseResults.get(i);
sumCost = sumCost+responseResult.getCostTime();
for (int percent : percents){
int percentNum = count*percent/100;
if(i == percentNum){
System.out.println(percent+"%响应时间为"+responseResult.getCostTime());
}
}
}
System.out.println("平均响应时间为"+sumCost/count);

}

public static void main(String[] args) {
String batchNo = UUID.randomUUID().toString();
PressTest pressTest = new PressTest(10, 100);
pressTest.sendRequest("https://xie.infoq.cn/", null, batchNo);
pressTest.analyseResult(batchNo);
}
}


10个并发,每个并发请求【https://xie.infoq.cn/】100次运行结果:

10%响应时间为322
20%响应时间为339
30%响应时间为353
40%响应时间为364
50%响应时间为373
60%响应时间为388
70%响应时间为400
80%响应时间为414
90%响应时间为438
平均响应时间为392



用户头像

elfkingw

关注

还未添加个人签名 2018.02.04 加入

还未添加个人简介

评论

发布
暂无评论
实现一个简易的压测工具