架构师训练营 - 作业 7
发布于: 2020 年 07 月 22 日
1.性能压测的时候,随着并发压力的增加,系统响应时间和吞吐量如何变化,为什么?
性能压测时,在性能测试,负载测试和压力测试时,系统响应时间变化如下图
性能压测时,在性能测试,负载测试和压力测试时,系统吞吐量变化如下图
由图可见,在性能测试阶段,系统响应时间近似不变,吞吐量近似于线性上升,在负载测试阶段,响应时间明显增大,吞吐量上升趋势减缓,在压力测试阶段,响应时间急剧增大,吞吐量下降直至系统崩溃。
其原因为:在性能测试阶段, 系统硬软件资源尚未完全被使用,可以通过增加系统软硬件资源的方式来响应不断增加的并发情求,在负载测试阶段,由于系统资源已经几乎完全被占用,所以会出现并发请求排队,系统线程调度等耗费,造成并发请求响应时间延长,吞吐量上升趋势减缓,到了压力测试阶段,由于系统资源耗尽,出现了大量并发请求拥堵等待,系统响应时间急剧增大,吞吐量下降直至系统不可用。
2.用你熟悉的语言编写一个 web 性能压测工具,输入参数:URL,请求总次数,并发数。输出参数:平均响应时间,95%响应时间。用这个测试工具以 10 并发,100 次请求压测 www.baidu.com。
代码如下:
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.net.URL;
import java.net.URLConnection;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.net.HttpURLConnection;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
public class TestPerformance implements Runnable{
public static Map<String, Long> map = new ConcurrentHashMap<String, Long>();
//每个线程的执行次数
private int totalCount;
//测试访问网站的URL
private String urlString;
//记录多线程的总执行次数,保证高并发下的原子性
public static AtomicInteger atomicInteger = new AtomicInteger(0);
public TestPerformance(int totalCount, String urlString) {
this.totalCount = totalCount;
this.urlString = urlString;
}
@Override
public void run() {
int count = 0;
while (count < totalCount) {
count++;
atomicInteger.getAndIncrement();
Instant now = Instant.now();
testConnectWebSite();
long used = ChronoUnit.MILLIS.between(now, Instant.now());
System.out.println("线程ID与对应的执行次数:" + Thread.currentThread().getId() + "--->" + count+" , 耗时"+used+"ms");
map.put("线程ID与对应的执行次数:" + Thread.currentThread().getId() + "--->" + count,used);
}
}
public void testConnectWebSite(){
try
{
URL url = new URL(urlString);
//连接网站
URLConnection urlConnection = url.openConnection();
HttpURLConnection connection = null;
if(!(urlConnection instanceof HttpURLConnection))
{
System.out.println("请输入正确 URL 地址");
return;
}
connection = (HttpURLConnection) urlConnection;
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
//打印网站输出内容,用于验证网站可正常访问
String contents = "";
String current;
while((current = in.readLine()) != null)
{
contents += current;
}
System.out.println(contents);
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
复制代码
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class StressTester {
public void doTest(int threadSize,int totalCount, String urlString) throws InterruptedException{
if (threadSize<1){
System.out.println("线程并发数量不能小于1");
return;
}
if (totalCount<1){
System.out.println("线程运行次数不能小于1");
return;
}
if ((urlString == null)||(urlString.equals(""))){
System.out.println("测试连接网站URL不能为空");
return;
}
//创建线程池
ExecutorService executorService = Executors.newFixedThreadPool(threadSize);
//让线程池中的每一个线程都开始工作
for (int j = 0; j < threadSize; j++) {
//执行线程
executorService.execute(new TestPerformance(totalCount,urlString));
}
//等线程全部执行完后关闭线程池
executorService.shutdown();
executorService.awaitTermination(Integer.MAX_VALUE, TimeUnit.DAYS);
List<Map.Entry<String, Long>> sortedEntryList = sortEntryByEntryValue(TestPerformance.map);
if (sortedEntryList.size()>1)
{
System.out.println("Performance test result show below:====================");
System.out.println("平均响应时间为:"+calculateAvgResponTime(sortedEntryList)+"ms");
System.out.println("95%请求响应时间为:"+calculateSpecifiedPercentageResponTime(sortedEntryList, 0.95)+"ms");
return;
}
System.out.println("未能获取测试数据");
}
//按照Entry的value(每次连接测试网站响应时间)大小排序并按照升序将Entry顺序放入list中
public LinkedList<Entry<String,Long>> sortEntryByEntryValue(Map<String, Long> map){
LinkedList<Map.Entry<String,Long>> list = new LinkedList<Map.Entry<String,Long>>(map.entrySet());
Comparator<Map.Entry<String,Long>> comparator = Comparator.comparing(Map.Entry::getValue);
Collections.sort(list,comparator);
return list;
}
//根据按照Entry的value(每次连接测试网站响应时间)大小排序的list计算平均响应时间
public Double calculateAvgResponTime(List<Map.Entry<String, Long>> list){
return list.stream().mapToLong(Map.Entry::getValue).average().getAsDouble();
}
//根据按照Entry的value(每次连接测试网站响应时间)大小排序的list和给定百分比计算百分比响应时间(即)
public Long calculateSpecifiedPercentageResponTime(List<Map.Entry<String, Long>> list,double percentage){
return list.get(new Long(Math.round(list.size()*percentage)).intValue()-1).getValue();
}
//模拟测试入口类
public static void main(String[] args) throws InterruptedException{
new StressTester().doTest(10, 100, "http://www.baidu.com");
}
}
复制代码
测试结果:
平均响应时间为:48.593ms
95%请求响应时间为: 95ms
划线
评论
复制
发布于: 2020 年 07 月 22 日阅读数: 62
进击的炮灰
关注
还未添加个人签名 2020.05.13 加入
还未添加个人简介
评论