同时用时间和次数控制压力测试(java多线程,可输出中间过程的计时结果)

之前对压测的认识比较肤浅,以为就是一个循环次数很大的for循环。
接下来的程序,可以分为四个部分(本例中业务线程只有写线程组,也可以继续添加其它业务线程):

  • 写线程组
  • 计时线程
  • 中间结果打印线程
  • 主函数

代码中没写import。MetricRegistry, Snapshot, Timer类来自com.codahale.metrics。
CountDownLatch和AtomicBoolean来自java.util.concurrent。
Logger和LoggerFactory来自org.slf4j。

首先是最重要的计时线程,其中最重要的是isStop变量,这个变量会被所有业务线程和主线程共享,起到全局控制作用。

public class TimeController implements Runnable {
	private waitSeconds;
	public static AtomicBoolean isStop = new AtomicBoolean(false);//初始值
	
	public TimeController(long waitSeconds) {
		if(waitSeconds == 0) {
			waitSeconds = Long.MAX_VALUE;
		}
	}

	@Override
	public void run() {
		while(waitSeconds > 0) {
			if(isStop.get() == true) {
				//提示信息
				break;
			}
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				//提示信息
			}
			waitSeconds--;
		}
		isStop.compareAndSet(false, true);//第一个参数是expect,第二个参数是update
	}
}

下面是写线程组中的写线程,可以替换为你所需要压力测试的代码。

public class WriteThread implements Runnable {
	private static final Logger LOG = LoggerFactory.getLogger(WriteThread.class);
	int threadNum;
	int yourLoopTimes;//为0时代表无限循环
	Timer timer;

	public WriteThread(int threadNum, int yourLoopTimes, Timer timer) {
		//略
	}

	@Override
	public void run() {
		int cnt = 0;
		if(yourLoopTimes == 0) {
			while(true) {
				if(TimeController.isStop.get() == true) {
					YourMain.waitThread.countDown();
					return;//唯一的退出方法就是等时限结束
				}

				Timer.Context ctx = timer.time();
				//
				//你需要计时的业务逻辑
				//
				ctx.stop();
			}
		}
	
		while(cnt < yourLoopTimes) {
			cnt++;
			if(TimeController.isStop.get() == true) {
				YourMain.waitThread.countDown();
				return;
			}
			
			Timer.Context ctx = timer.time();
			//
			//你需要计时的业务逻辑
			//
			ctx.stop();
		}
	}
}

下面是打印线程。

public class MetricsPrintController implements Runnable {
	private static final Logger LOG = LoggerFactory.getLogger(MetricsPrintController.class);
	long printInterval;
	//此处应该包含所有业务线程的timer。当然,如果有的业务线程不需要输出中间测量结果,也可以不加在这
	Timer writeTimer;

	public MetricsPrintController(long printInterval, Timer writeTimer) {
		//略
	}

	@Override
	public void run(){
		if(printInterval<=0){
			return;
		}
		while(true) {
			if(TimerController.isStop.get() == true) {
				break;
			}
			try {
				Thread.sleep(printInterval);
				if(TimerController.isStop.get() == true) {
					break;
				}
			} catch (InterruptedException e) {
				//你的异常处理
			}
			Snapshot writeSnapshot = writeTimer.getSnapshot();
			System.out.println(writeTimer.getMeanRate());
			System.out.println(writeSnapshot.get99thPercentile()/(1000*1000));
		}
	}
}

下面是主函数。

public class YourMain {
	private static final Logger LOG = LoggerFactory.getLogger(YourMain.class);

	public static Timer makeTimer(Class<?> class, String name) {
		MetricRegistry registry = new MetricRegistry();
		Timer timer = registry.timer(MetricRegistry.name(klass, name));
	}

	public static void main(String[] args) {
		//main的输入参数处理,略
		//所有业务线程数之和,比如读线程组大小+写线程组大小,不包括计时线程和打印线程
		int sumOfYourExecuteThread = 1234567;//正常来说是由main参数传入
		int yourLoopTimes = 1234567;//正常来说是由main参数传入
		int yourTestTime = 1234567;//正常来说是由main参数传入
		int writeThreadNum = 1234567;//正常来说是由main参数传入
		int printInterval = 1234567;//正常来说是由main参数传入
		//
		
		//如果还有其它业务线程组,此处每个业务线程组都要有一个Timer
		Timer writeTimer = makeTimer(YourMain.class, "yourTimerName");
		
		waitThread = new CountDownLatch(sumOfYourExecuteThread);
		
		//-----------------------------------------------------------------------------
		//启动写线程
		for(int i = 0; i < writeThreadNum; i++){
			WriteThread yourWriteThreadName = new WriteThread(i, yourLoopTimers, writeTimer);
			Thread thread = new Thread(yourWriteThreadName, "yourWriteThreadName-" + i);
			thread.start();
		}

		//-----------------------------------------------------------------------------
		//启动计时线程
		TimeController timerControllerThread = new TimeController(yourTestTime);
		THread thread = new Thread(timerControllerThread, "timerControllerThread ");
		thread.start();

		//-----------------------------------------------------------------------------
		//启动打印线程,传入所有业务线程组的timer
		MetricsPrintController metricsPrintTHread = new MetricsPrintController(printInterval ,writeTimer);
		Thread metricPrintThread = new Thread(metricsPrintThread, "metricsPrintThread");
		metricPrintThread.start();

		//-----------------------------------------------------------------------------
		try {
			waitTHread.await();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		//如果以下语句顺利执行,说明所有业务线程通过次数结束了,以下语句顺利执行后计时线程和打印线程也会停止。如果计时线程先结束,则以下语句不会顺利执行。
		boolean res = isStop.compareAndSet(false, true);//expect, update

		//-----------------------------------------------------------------------------
		Snapshot snapshot = writeTimer.getSnapshot();
		System.out.println(writeTimer.getMeanRate());
		System.out.println(snapshot.get99thPercentile()/(1000*1000));
	}
}

附录

压测除了计时,更重要的其实是观察硬件资源使用,否则就只是普通的性能测试。

cpu指标

  • cpu.busy
  • cpu.iowait
  • cpu.irq
  • cpu.softirq
  • cpu.switches
  • cpu.system
  • cpu.user

mem use指标

  • mem.memused
  • mem.memused.percent
  • mem.swapused
  • mem.swapused.percent

net指标

如果是向外发送的话,主要看net.if.out.bytes

内存使用分析

注意jmap会导致java进程挂起,线上环境慎用!
内存分析工具mat安装和使用教程:http://www.moheqionglin.com/site/blogs/84/detail.html

wget http://eclipse.stu.edu.tw/mat/1.9.0/rcp/MemoryAnalyzer-1.9.0.20190605-linux.gtk.x86_64.zip

jmap -dump:format=b,file=jmap.info PID

./ParseHeapDump.sh jmap.info  org.eclipse.mat.api:suspects org.eclipse.mat.api:overview org.eclipse.mat.api:top_components

结果中会揭示最大的类,最大的对象,以及类加载器相关的信息。
最大的对象可能是一个容器类如map,最大的类可能是容器类存储的那个类。
不同类加载器的资源消耗会被统计,类如果被不同类加载器重复加载,也会被发现。
有多少软引用?有多少弱引用?有多少对象实现了finalize方法?map的碰撞率如何?
至于内存浪费,考虑到重复字符串,空集合和低填充率集合。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值