架构师课作业 - 第七周

用户头像
Tulane
关注
发布于: 2020 年 07 月 22 日

作业:

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



答:

  • 随着并发压力从0开始增加, 分为三个阶段, 性能测试阶段、负载测试阶段、压力测试阶段

  • 性能测试阶段: 每项系统资源都充裕, 系统响应时间基本保持不变, 而吞吐量则线性上升

  • 负载测试阶段: 系统资源仍然有余, 可能由于并发导致的线程上下文切换影响了系统开销, 系统响应时间会略微上升, 而吞吐量基本也呈线性上升

  • 压力测试阶段: 此时系统资源的某一项或多项到达瓶颈, 系统响应时间会极速上升, 吞吐量也会极速下降, 直到系统崩溃






作业:

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



分析:

  • 给予持续的10并发压力, 总共请求100次

  • 使用 Semaphore信号量 确保并发数 (实际应该可以不用, 线程池已确保)

  • 使用 CountDownLatch计数器 阻塞等待所有线程完成, 再执行统计

  • Http工具使用池化减少响应时间影响





package com.tulane.hatch.pressure;
import com.tulane.hatch.util.HttpUtils;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
/**
* 压测
*/
public class PressureTest {
private String url;
private int requestNum;
private int concurrentNum;
private SortedMap<Long, Integer> timeMap;
public PressureTest(String url, int requestNum, int concurrentNum) {
this.url = url;
this.requestNum = requestNum;
this.concurrentNum = concurrentNum;
timeMap = new TreeMap<>();
}
public void doExcute(){
ExecutorService executorService = Executors.newFixedThreadPool(concurrentNum);
Semaphore semaphore = new Semaphore(concurrentNum);
CountDownLatch countDownLatch = new CountDownLatch(requestNum);
concurrentRequest(executorService, semaphore, countDownLatch);
// 计算时间
long avgTime = mathAvgTime();
long majorityTime = mathMajorityTime();
System.out.println("请求数为: " + requestNum + " , 并发数为: " + concurrentNum + " , 平均时间: " + avgTime + " , 95%响应时间: " + majorityTime);
}
private void concurrentRequest(ExecutorService executorService, Semaphore semaphore, CountDownLatch countDownLatch) {
for (int i = 0; i < requestNum; i++) {
try {
semaphore.acquire();
executorService.execute(() -> {
long startTime = System.currentTimeMillis();
HttpUtils.get(url);
long endTime = System.currentTimeMillis();
synchronized (timeMap.getClass()){
Long key = endTime - startTime;
final Integer num = timeMap.getOrDefault(key, 0);
timeMap.put(key, num + 1);
}
semaphore.release();
countDownLatch.countDown();
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
countDownLatch.await();
executorService.shutdown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/**
* 计算平均时间
*/
private long mathAvgTime() {
Long sumTime = 0l;
int count = 0;
for (Map.Entry<Long, Integer> entry : timeMap.entrySet()) {
for (Integer i = 0; i < entry.getValue(); i++) {
sumTime += entry.getKey();
count++;
}
}
return sumTime / count;
}
/**
* 计算95%响应时间
*/
private long mathMajorityTime() {
int count = 0;
for (Map.Entry<Long, Integer> entry : timeMap.entrySet()) {
for (Integer i = 0; i < entry.getValue(); i++) {
count++;
}
}
int start = 0;
int end = (int) (count / 100D * 95D);
for (Map.Entry<Long, Integer> entry : timeMap.entrySet()) {
for (Integer i = 0; i < entry.getValue(); i++) {
if(++start == end){
return entry.getKey();
}
}
}
return 0l;
}
public static void main(String[] args) {
final PressureTest pressureTest = new PressureTest("https://www.baidu.com", 100, 10);
pressureTest.doExcute();
}
}



HttpUtils

package com.tulane.hatch.util;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
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.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
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.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class HttpUtils {
private static CloseableHttpClient httpClient;
static {
try {
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build()); // 配置同时支持 HTTP 和 HTPPS
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", sslsf)
.build();
PoolingHttpClientConnectionManager poolConnManager = new PoolingHttpClientConnectionManager(
socketFactoryRegistry);
poolConnManager.setMaxTotal(200);
poolConnManager.setDefaultMaxPerRoute(100);
int socketTimeout = 2000;
int connectTimeout = 2000;
int connectionRequestTimeout = 2000;
RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(connectionRequestTimeout).setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
httpClient = HttpClients.custom().setConnectionManager(poolConnManager).setDefaultRequestConfig(requestConfig).setRetryHandler(new DefaultHttpRequestRetryHandler(0, false)).build();
} catch (Exception e) {
}
}
public static String get(String url) {
return get(url, null, null);
}
public static String get(String url, Map<String, Object> params) {
return get(url, params, null);
}
public static String get(String url, Map<String, Object> params, Map<String, String> headers) {
StringBuilder requestUrl = new StringBuilder(url);
if (params != null) {
requestUrl.append("?");
for (Map.Entry<String, Object> entry : params.entrySet()) {
requestUrl.append(entry.getKey()).append("=").append(String.valueOf(entry.getValue()));
requestUrl.append("&");
}
}
CloseableHttpResponse response = null;
String result = null;
HttpGet httpGet = new HttpGet(requestUrl.toString());
if (headers != null) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
httpGet.addHeader(entry.getKey(), entry.getValue());
}
}
try {
response = httpClient.execute(httpGet);
HttpEntity entity = response.getEntity();
if (entity != null) {
result = EntityUtils.toString(entity);
}
} catch (IOException e) {
} finally {
httpGet.abort();
try {
if (response != null) {
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
public static String postJson(String url) {
return postJson(url, null, null);
}
public static String postJson(String url, String json) {
return postJson(url, json, null);
}
public static String postJson(String url, String json, Map<String, String> headers) {
HttpPost httpPost = new HttpPost(url);
if (headers != null) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
httpPost.addHeader(entry.getKey(), entry.getValue());
}
}
if (json != null) {
StringEntity stringEntity = new StringEntity(json, ContentType.APPLICATION_JSON);
httpPost.setEntity(stringEntity);
}
String result = null;
CloseableHttpResponse response = null;
try {
response = httpClient.execute(httpPost);
HttpEntity entity = response.getEntity();
if (entity != null) {
result = EntityUtils.toString(entity);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
httpPost.abort();
try {
if (response != null) {
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
public static String postForm(String url) {
return postForm(url, null, null);
}
public static String postForm(String url, Map<String, String> params) {
return postForm(url, params, null);
}
public static String postForm(String url, Map<String, String> params, Map<String, String> headers) {
HttpPost httpPost = new HttpPost(url);
String result = null;
CloseableHttpResponse response = null;
try {
if (headers != null) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
httpPost.addHeader(entry.getKey(), entry.getValue());
}
}
if (params != null) {
List<NameValuePair> paramPairs = new ArrayList<NameValuePair>();
for (Map.Entry<String, String> entry : params.entrySet()) {
paramPairs.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramPairs, "UTF-8");
httpPost.setEntity(entity);
}
response = httpClient.execute(httpPost);
HttpEntity entity = response.getEntity();
if (entity != null) {
result = EntityUtils.toString(entity);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
httpPost.abort();
try {
if (response != null) {
response.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
}



用户头像

Tulane

关注

还未添加个人签名 2018.09.18 加入

还未添加个人简介

评论

发布
暂无评论
架构师课作业 - 第七周