在上一次,我們分享了一個壓力測試的模板類,在模板類裏面通過繼承然後實現重寫幾個方法即可以實現壓力測試,其本質就是由實現者去實現具體的測試邏輯,將功能代碼獨立抽離出來實現複用。然而,繼承存在着一些缺點是無法避免的。比如具體的失去了靈活性,而且如果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();
}
}
在這裏只需要關心線程數和業務邏輯了,不需要繼承任何類,歡迎討論。