Java工程进行性能测试时通常要考虑哪些要素,如何使接口响应时间在毫秒级范围,Java项目如何进行性能测试?0.2s的反应时间内返回响应,用户感知情况较好。附IO密集型和计算密集型性能测试摸底代码

15 篇文章 0 订阅

        研究表明,人的反应时间通常在0.2s左右,运动员0.14s已经是顶级反应了,所以响应时间在0.2秒以内通常是无感的。那么在性能测试时,需要关注哪些指标呢?

CPU:有的应用需要大量计算,他们会长时间、不间断地占用 CPU 资源,导致其他资源无法争夺到 CPU 而响应缓慢,从而带来系统性能问题。例如,代码递归导致的无限循环,正则表达式引起的回溯,JVM 频繁的 FULL GC,以及多线程编程造成的大量上下文切换等,这些都有可能导致 CPU 资源繁忙。

内存Java 程序一般通过 JVM 对内存进行分配管理,主要是用 JVM 中的堆内存来存储 Java 创建的对象。系统堆内存的读写速度非常快,所以基本不存在读写性能瓶颈。但是由于内存成本要比磁盘高,相比磁盘,内存的存储空间又非常有限。所以当内存空间被占满,对象无法回收时,就会导致内存溢出、内存泄露等问题。

磁盘 I/O:磁盘相比内存来说,存储空间要大很多,但磁盘 I/O 读写的速度要比内存慢,虽然目前引入的 SSD 固态硬盘已经有所优化,但仍然无法与内存的读写速度相提并论。

网络:网络对于系统性能来说,也起着至关重要的作用。如果你购买过云服务,一定经历过,选择网络带宽大小这一环节。带宽过低的话,对于传输数据比较大,或者是并发量比较大的系统,网络就很容易成为性能瓶颈。

异常:Java 应用中,抛出异常需要构建异常栈,对异常进行捕获和处理,这个过程非常消耗系统性能。如果在高并发的情况下引发异常,持续地进行异常处理,那么系统的性能就会明显地受到影响。数据库:大部分系统都会用到数据库,而数据库的操作往往是涉及到磁盘 I/O 的读写。大量的数据库读写操作,会导致磁盘 I/O 性能瓶颈,进而导致数据库操作的延迟性。对于有大量数据库读写操作的系统来说,数据库的性能优化是整个系统的核心。

锁竞争:在并发编程中,我们经常会需要多个线程,共享读写操作同一个资源,这个时候为了保持数据的原子性(即保证这个共享资源在一个线程写的时候,不被另一个线程修改),我们就会用到锁。锁的使用可能会带来上下文切换,从而给系统带来性能开销。JDK1.6 之后,Java 为了降低锁竞争带来的上下文切换,对 JVM 内部锁已经做了多次优化,例如,新增了偏向锁、自旋锁、轻量级锁、锁粗化、锁消除等。而如何合理地使用锁资源,优化锁资源,就需要你了解更多的操作系统知识、Java 多线程编程基础,积累项目经验,并结合实际场景去处理相关问题。了解了上面这些基本内容,我们可以得到下面几个指标,来衡量一般系统的性能。响应时间响应时间是衡量系统性能的重要指标之一,响应时间越短,性能越好,一般一个接口的响应时间是在毫秒级

 操作数据库的时间是请求接口中最耗时的操作,是操作内存的数个数量级耗时。

计算机资源分配使用率

通常由 CPU 占用率、内存使用率、磁盘 I/O、网络 I/O 来表示资源使用率。这几个参数好比一个木桶,如果其中任何一块木板出现短板,任何一项分配不合理,对整个系统性能的影响都是毁灭性的。

负载承受能力

当系统压力上升时,你可以观察,系统响应时间的上升曲线是否平缓。这项指标能直观地反馈给你,系统所能承受的负载压力极限。例如,当你对系统进行压测时,系统的响应时间会随着系统并发数的增加而延长,直到系统无法处理这么多请求,抛出大量错误时,就到了极限。

数据库响应时间:数据库操作所消耗的时间,往往是整个请求链中最耗时的;

服务端响应时间:服务端包括 Nginx 分发的请求所消耗的时间以及服务端程序执行所消耗的时间;

网络响应时间:这是网络传输时,网络硬件需要对传输的请求进行解析等操作所消耗的时间;

客户端响应时间:对于普通的 Web、App 客户端来说,消耗时间是可以忽略不计的,但如果你的客户端嵌入了大量的逻辑处理,消耗的时间就有可能变长,从而成为系统的瓶颈。

吞吐量

在测试中,我们往往会比较注重系统接口的 TPS(每秒事务处理量),因为 TPS 体现了接口的性能,TPS 越大,性能越好。在系统中,我们也可以把吞吐量自下而上地分为两种:磁盘吞吐量和网络吞吐量。

性能测试有哪些干扰因子?

了解了上述的关键指标后,性能测试就有了相应关注点。

精准定位哪个模块或者接口的性能问题,可以微基准性能测试,即不同实现方式下的性能对比。

考虑测试环境、测试场景和测试目标,可以通过宏基准性能测试。

模拟线上环境,然后看测试场景。我们需要确定在测试某个接口时,是否有其他业务接口同时也在平行运行,造成干扰。如果有,请重视,因为你一旦忽视了这种干扰,测试结果就会出现偏差。最后看测试目标。我们的性能测试是要有目标的,这里可以通过吞吐量以及响应时间来衡量系统是否达标。不达标,就进行优化;达标,就继续加大测试的并发数,探底接口的 TPS(最大每秒事务处理量),这样做,可以深入了解到接口的性能。观察服务器的 CPU、内存以及 I/O 使用率的变化。

性能测试时,系统会运行得越来越快,后面的访问速度要比我们第一次访问的速度快上几倍。热点代码,JVM会通过即时编译器(JIT compiler,just-in-time compiler)把这些代码编译成与本地平台相关的机器码,并进行各层次的优化,然后存储在内存中,之后每次运行代码时,直接从内存中获取。

多 JVM 情况下的影响:服务器有多个Java项目,部署在不同的 Tomcat 中,这就意味着服务器会有多个 JVM。任意一个  JVM 都拥有整个系统的资源使用权。原则上一台机器上只部署单独的一个 JVM,在做性能测试时,测试结果干扰少。所以我们应该避免环境中一台机器部署多个  JVM  的情况。

 限流,对系统的入口设置最大访问限制。参考性能测试中探底接口的 TPS 。同时采取熔断措施,友好地返回没有成功的请求。

计算密集型,线程池参数调优,https://github.com/nickliuchao/threadpollsizetest,核心线程数一般设定为N+1,比 CPU 核心数多出来的一个线程是为了防止线程偶发的缺页中断,或者其它原因导致的任务暂停而带来的影响。IO密集型2N,实际情况根据线上压力进行增加或者减少核心线程数。

千万别漏了GC日志

-XX:+PrintGCDateStamps -XX:+PrintGCDetails -Xloggc:./gclogs
 

 解释:

-XX:+PrintGCDateStamps 日期格式打印GC时间

-XX:+PrintGCDetails GC详细信息

-Xloggc:./gc.log GC日志 会根据系统自动修改/

jmeter测试注意事项:

线程名:IoT性能吞吐控制(带集合点。8:2读写并发) 测试任务集合1

线程名:IoT性能(带集合点。不同时进行读写)测试任务集合2

线程名:IoT性能无集合点(不带集合点)测试任务集合3

step1: 启用要测试的线程,禁用不测试的线程 只能跑一个任务,否则任务之间会彼此影响

step2:修改并发数 1)线程组下的“线程数” 2)用户自定义的变量中,sample 变量名,8:2的还要修改sample20%、sample80% 两个变量 3)用户登录数据文件位置要修改

step3:聚合报告修改运行结果保存路径 文件名后缀.jtl 【E:\项目\IoT\iot文档\username.jtl】 注意: 1、每跑一次,就改下文件名后缀,否则运行结果可能会覆盖或叠加 2、每次跑前点下清除或清楚全部按钮,把记录清除

IO测试 

public class IOTypeTest implements Runnable {


	//整体执行时间,包括在队列中等待的时间
	Vector<Long> wholeTimeList;
	//真正执行时间
	Vector<Long> runTimeList;
	
	private long initStartTime = 0;
	
	/**
	 * 构造函数
	 * @param runTimeList
	 * @param wholeTimeList
	 */
	public IOTypeTest(Vector<Long> runTimeList, Vector<Long> wholeTimeList) {
		initStartTime = System.currentTimeMillis();
		this.runTimeList = runTimeList;
		this.wholeTimeList = wholeTimeList;
	}
	
	/**
	 *IO操作
	 * @param number
	 * @return
	 * @throws IOException 
	 */
	public void readAndWrite() throws IOException {
		File sourceFile = new File("D:/tets.txt");
        //创建输入流
        BufferedReader input = new BufferedReader(new FileReader(sourceFile));
        //读取源文件,写入到新的文件
        String line = null;
        while((line = input.readLine()) != null){
            System.out.println(line);
        }
        //关闭输入输出流
        input.close();
	}

	public void run() {
		long start = System.currentTimeMillis();
		try {
			try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			readAndWrite();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		long end = System.currentTimeMillis();

		long wholeTime = end - initStartTime;
		long runTime = end - start;
		wholeTimeList.add(wholeTime);
		runTimeList.add(runTime);
		System.out.println("单个线程花费时间:" + (end - start));
	}
	

}

CPU测试



import java.util.List;

public class CPUTypeTest implements Runnable {

	//整体执行时间,包括在队列中等待的时间
	List<Long> wholeTimeList;
	//真正执行时间
	List<Long> runTimeList;
	
	private long initStartTime = 0;
	
	/**
	 * 构造函数
	 * @param runTimeList
	 * @param wholeTimeList
	 */
	public CPUTypeTest(List<Long> runTimeList, List<Long> wholeTimeList) {
		initStartTime = System.currentTimeMillis();
		this.runTimeList = runTimeList;
		this.wholeTimeList = wholeTimeList;
	}
	
	/**
	 * 判断素数
	 * @param number
	 * @return
	 */
	public boolean isPrime(final int number) {
		if (number <= 1)
			return false;

		for (int i = 2; i <= Math.sqrt(number); i++) {
			if (number % i == 0)
				return false;
		}
		return true;
	}

	/**
	 * 計算素数
	 * @param number
	 * @return
	 */
	public int countPrimes(final int lower, final int upper) {
		int total = 0;
		for (int i = lower; i <= upper; i++) {
			if (isPrime(i))
				total++;
		}
		return total;
	}

	public void run() {
		long start = System.currentTimeMillis();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		countPrimes(1, 1000000);
		long end = System.currentTimeMillis();

		long wholeTime = end - initStartTime;
		long runTime = end - start;
		wholeTimeList.add(wholeTime);
		runTimeList.add(runTime);
		System.out.println("单个线程花费时间:" + (end - start));
	}

}
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * Hello world!
 *
 */
public class App {
	// 初始化线程池
	private static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(8, 8, 10, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(1000), new ThreadPoolExecutor.DiscardOldestPolicy());

	public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
		
		int cores = Runtime.getRuntime().availableProcessors();

		int requestNum = 100;
		System.out.println("CPU核数 " + cores);

		List<Future<?>> futureList = new ArrayList<Future<?>>();
		
		Vector<Long> wholeTimeList = new Vector<Long>();
		Vector<Long> runTimeList = new Vector<Long>();
		
		for (int i = 0; i < requestNum; i++) {
			Future<?> future = threadPool.submit(new CPUTypeTest(runTimeList, wholeTimeList));
			
			//Future<?> future = threadPool.submit(new IOTypeTest(runTimeList, wholeTimeList));
			futureList.add(future);
		}

		for (Future<?> future : futureList) {
			//获取线程执行结果
			future.get(requestNum, TimeUnit.SECONDS);
		}
		
		long wholeTime = 0;
		for (int i = 0; i < wholeTimeList.size(); i++) {
			wholeTime = wholeTimeList.get(i) + wholeTime;
		}
		
		long runTime = 0;
		for (int i = 0; i < runTimeList.size(); i++) {
			runTime = runTimeList.get(i) + runTime;
		}

		System.out.println("平均每个线程整体花费时间: " +wholeTime/wholeTimeList.size());
		System.out.println("平均每个线程执行花费时间: " +runTime/runTimeList.size());
	}
}
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;

/**
 * Unit test for simple App.
 */
public class AppTest extends TestCase {
    /**
     * Create the test case
     *
     * @param testName name of the test case
     */
    public AppTest( String testName ) {
        super( testName );
    }

    /**
     * @return the suite of tests being tested
     */
    public static Test suite() {
        return new TestSuite( AppTest.class );
    }

    /**
     * Rigourous Test :-)
     */
    public void testApp() {
        assertTrue( true );
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值