架构师课作业 - 第七周
发布于: 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; }}
划线
评论
复制
发布于: 2020 年 07 月 22 日阅读数: 59
Tulane
关注
还未添加个人签名 2018.09.18 加入
还未添加个人简介
评论