在上一次,我们分享了一个压力测试的模板类,在模板类里面通过继承然后实现重写几个方法即可以实现压力测试,其本质就是由实现者去实现具体的测试逻辑,将功能代码独立抽离出来实现复用。然而,继承存在着一些缺点是无法避免的。比如具体的失去了灵活性,而且如果java不支持多继承,意味着测试类会被限制少了继承其他类的能力,再者之前的代码对外部暴露的接口过多,无法对用户实现彻底的透明。因此对原先的代码采用了Function方式进行重构。
Function的方式即将函数作为参数传入方法中,由具体的调用者去决定函数的内容,这个刚好是我们需要的,由具体的测试者去实现需要测试的逻辑。可惜在java中并不支持将函数作为参数传入方法中,不能像js那样 function xxx(functionx)去声明调用。不过,在设计模式中有一种叫做命令模式的,我们简单的借用下。
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
在java的java.util下有这个Runable的接口,只需要实现run方法即可。
public void xxx(Runable r){
r.run();
}
如果有疑问function不是一个接口吗,那apply方法哪里实现的,这样想就对了。如果要体会这种写法怎么做,可以直接new Function()xxxx看看IDE会怎么帮你自动生成一些代码。这个就是由调用xxx的用户去实现function的内容,在运行该方法的处决定function接口的内容,将实现者和调用者解耦。接下来是重构过的测试模板类。正确的说不是模板类了,是直接可以运行的功能类了。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;
/**
* 通用压力测试类
* Created by lsz on 14-10-24.
*/
public class CommonsTest {
int threadNum = 1;
int sleepTime = 1000;
long lastTimes = 0;
int x = 0;
private AtomicLong now = new AtomicLong();
private AtomicLong maxTime = new AtomicLong(0l);
ExecutorService pool = null;
Runnable runnable;
public CommonsTest(int threadNum,Runnable runnable) {
this.runnable = runnable;
this.threadNum = threadNum;
pool = Executors.newScheduledThreadPool(this.threadNum);
}
CommonsTest(int threadNum){
this(threadNum,null);
}
CommonsTest(Runnable runnable){
this(5,runnable);
}
public static CommonsTest create(){
return new CommonsTest(5);
}
public CommonsTest setThreadNum(int threadNum){
this.threadNum = threadNum;
return this;
}
public CommonsTest setApply(Runnable runnable){
this.runnable = runnable;
return this;
}
public void start(){
for(int i = 0 ; i<threadNum;i ++ ){
pool.execute(new TestThread());
}
String format = "---------------------"+"当前1s内的执行数"+"/"+"总的执行数"+"/"+"平均的执行数"+"/"+"执行的时间"+"/"+"最长执行时间";
while(true){
try {
x++;
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
long nowtmp = now.get();
System.out.println(format);
System.out.println("---------------------"+(nowtmp - lastTimes)+"/"+now.intValue()+"/"+(now.intValue()/x)+"/"+x+"/"+maxTime.get()+"ms");
lastTimes = nowtmp;
}
}
class TestThread extends Thread{
public TestThread() {
}
@Override
public void run() {
System.out.println("start------------------"+Thread.currentThread().getName());
while (true){
try {
long start = System.currentTimeMillis();
runnable.run();
long end = System.currentTimeMillis() - start;
if(end>maxTime.get()){
maxTime.set(end);
}
now.incrementAndGet();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
看到这个类,感觉和之前的模板类没啥区别,只是在具体的测试代码调用处从模版方法改成了接口方法。具体的使用也简单了n多。
/**
* Created by lsz on 14-10-24.
*/
public class SimpleTest {
public static void main(String[] args) {
new CommonsTest(5,new Runable() {
@Override
public void run {
//这里写压力测试的逻辑代码
// 创建http请求(get方式)
// HttpGet httpget = new HttpGet("http://www.xxx.com/xxx");
//httpclient.execute(httpget);
return true;
}
}).start();
}
}
在这里只需要关心线程数和业务逻辑了,不需要继承任何类,欢迎讨论。