手寫java性能測試框架第二版
引言
依照領導要求區分瞭兩種壓測模式:固定次數壓測和固定時間壓測。此前一直沿用的都是固定次數,所以本次第二版剝離瞭固定次數的模式增加瞭固定時間的模式。
這是第一版:性能測試框架
第二版的threadbase代碼如下
package com.fun.base.constaint; import com.fun.frame.SourceCode; import java.util.concurrent.CountDownLatch; /** * 多線程任務基類,可單獨使用 */ public abstract class ThreadBase<T> extends SourceCode implements Runnable { /** * 計數鎖 * <p> * 會在concurrent類裡面根據線程數自動設定 * </p> */ CountDownLatch countDownLatch; /** * 用於設置訪問資源 */ public T t; protected ThreadBase() { super(); } /** * groovy無法直接訪問t,所以寫瞭這個方法 * * @return */ public String getT() { return t.toString(); } /** * 運行待測方法的之前的準備 */ protected abstract void before(); /** * 待測方法 * * @throws Exception */ protected abstract void doing() throws Exception; /** * 運行待測方法後的處理 */ protected abstract void after(); public void setCountDownLatch(CountDownLatch countDownLatch) { this.countDownLatch = countDownLatch; } }
固定次數模式的壓測虛擬類
package com.fun.base.constaint; import com.fun.frame.excute.Concurrent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; import static com.fun.utils.Time.getTimeStamp; /** * 請求時間限制的多線程類,限制每個線程執行的次數 * * <p> * 通常在測試某項用例固定時間的場景下使用,可以提前終止測試用例 * </p> * * @param <T> 閉包參數傳遞使用,Groovy腳本會有一些兼容問題,部分對象需要tostring獲取參數值 */ public abstract class ThreadLimitTimes<T> extends ThreadBase { private static final Logger logger = LoggerFactory.getLogger(ThreadLimitTimes.class); /** * 任務請求執行次數 */ public int times; /** * 用於設置訪問資源 */ public T t; public ThreadLimitTimes(T t, int times) { this(times); this.t = t; } public ThreadLimitTimes(int times) { this(); this.times = times; } protected ThreadLimitTimes() { super(); } /** * groovy無法直接訪問t,所以寫瞭這個方法 * * @return */ public String getT() { return t.toString(); } @Override public void run() { try { before(); List<Long> t = new ArrayList<>(); long ss = getTimeStamp(); for (int i = 0; i < times; i++) { long s = getTimeStamp(); doing(); long e = getTimeStamp(); t.add(e - s); } long ee = getTimeStamp(); logger.info("執行次數:{},總耗時:{}", times, ee - ss); Concurrent.allTimes.addAll(t); } catch (Exception e) { logger.warn("執行任務失敗!", e); } finally { if (countDownLatch != null) countDownLatch.countDown(); after(); } } /** * 運行待測方法的之前的準備 */ protected abstract void before(); /** * 待測方法 * * @throws Exception */ protected abstract void doing() throws Exception; /** * 運行待測方法後的處理 */ protected abstract void after(); }
固定時間模式虛擬類
package com.fun.base.constaint; import com.fun.frame.excute.Concurrent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; import static com.fun.utils.Time.getTimeStamp; /** * 請求時間限制的多線程類,限制每個線程執行的時間 * <p> * 通常在測試某項用例固定時間的場景下使用,可以提前終止測試用例 * </p> * * @param <T> 閉包參數傳遞使用,Groovy腳本會有一些兼容問題,部分對象需要tostring獲取參數值 */ public abstract class ThreadLimitTime<T> extends ThreadBase { /** * 全局的時間終止開關 */ private static boolean key = false; private static final Logger logger = LoggerFactory.getLogger(ThreadLimitTime.class); /** * 任務請求執行時間,單位是秒 */ public int time; /** * 用於設置訪問資源 */ public T t; public ThreadLimitTime(T t, int time) { this(time); this.t = t; } public ThreadLimitTime(int time) { this(); this.time = time * 1000; } protected ThreadLimitTime() { super(); } @Override public void run() { try { before(); List<Long> t = new ArrayList<>(); long ss = getTimeStamp(); while (true) { long s = getTimeStamp(); doing(); long e = getTimeStamp(); t.add(e - s); if ((e - ss) > time || key) break; } long ee = getTimeStamp(); logger.info("執行時間:{} s,總耗時:{}", time / 1000, ee - ss); Concurrent.allTimes.addAll(t); } catch (Exception e) { logger.warn("執行任務失敗!", e); } finally { if (countDownLatch != null) countDownLatch.countDown(); after(); } } /** * 用於在某些情況下提前終止測試 */ public static void stopAllThread() { key = true; } }
這裡我多加瞭一個終止測試的key,暫時沒有用,以防萬一。之所以沒有采用另起線程去計時原因有二:進行測試過程中無論如何都會記錄時間戳,多餘的計算比較時間戳大小消耗性能很低,可以忽略;另起線程設計麻煩,在發生意外情況時缺少第二種保險措施。
HTTPrequestbase為基礎的多線程類
下面是兩種實現類的Demo,以HTTPrequestbase作為基礎的多線程類。
固定次數模式的多線程類
/** * http請求多線程類 */ public class RequestThreadTimes extends ThreadLimitTimes { static Logger logger = LoggerFactory.getLogger(RequestThreadTimes.class); /** * 請求 */ public HttpRequestBase request; /** * 單請求多線程多次任務構造方法 * * @param request 被執行的請求 * @param times 每個線程運行的次數 */ public RequestThreadTimes(HttpRequestBase request, int times) { this.request = request; this.times = times; } @Override public void before() { GCThread.starts(); } @Override protected void doing() throws Exception { getResponse(request); } @Override protected void after() { GCThread.stop(); } /** * 多次執行某個請求,但是不記錄日志,記錄方法用 loglong * <p>此方法隻適應與單個請求的重復請求,對於有業務聯系的請求暫時不能適配</p> * * @param request 請求 * @throws IOException */ void getResponse(HttpRequestBase request) throws IOException { CloseableHttpResponse response = ClientManage.httpsClient.execute(request); String content = FanLibrary.getContent(response); if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) logger.warn("響應狀態碼:{},響應內容:{}", content, response.getStatusLine()); response.close(); } }
固定時間模式的多線程類
package com.fun.frame.thead; import com.fun.base.constaint.ThreadLimitTime; import com.fun.frame.httpclient.ClientManage; import com.fun.frame.httpclient.FanLibrary; import com.fun.frame.httpclient.GCThread; import org.apache.http.HttpStatus; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpRequestBase; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; /** * http請求多線程類 */ public class RequestThreadTime extends ThreadLimitTime { static Logger logger = LoggerFactory.getLogger(RequestThreadTime.class); /** * 請求 */ public HttpRequestBase request; /** * 單請求多線程多次任務構造方法 * * @param request 被執行的請求 * @param times 每個線程運行的次數 */ public RequestThreadTime(HttpRequestBase request, int time) { this.request = request; this.time = time; } @Override public void before() { GCThread.starts(); } @Override protected void doing() throws Exception { getResponse(request); } @Override protected void after() { GCThread.stop(); } /** * 多次執行某個請求,但是不記錄日志,記錄方法用 loglong * <p>此方法隻適應與單個請求的重復請求,對於有業務聯系的請求暫時不能適配</p> * * @param request 請求 * @throws IOException */ void getResponse(HttpRequestBase request) throws IOException { CloseableHttpResponse response = ClientManage.httpsClient.execute(request); String content = FanLibrary.getContent(response); if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) logger.warn("響應狀態碼:{},響應內容:{}", content, response.getStatusLine()); response.close(); } }
其中可以發現,差別就在於屬性time還是times的設定。
使用Demo:
package com.fun; import com.fun.base.constaint.ThreadLimitTime; import com.fun.frame.SourceCode; import com.fun.frame.excute.Concurrent; import java.util.ArrayList; import java.util.List; public class AR extends SourceCode { public static void main(String[] args) { ThreadLimitTime<Object> threadLimitTime = new ThreadLimitTime<Object>(10) { /** * 運行待測方法的之前的準備 */ @Override protected void before() { } /** * 待測方法 * * @throws Exception */ @Override protected void doing() throws Exception { AR.test(); } /** * 運行待測方法後的處理 */ @Override protected void after() { } }; new Concurrent(threadLimitTime,5).start(); FanLibrary.testOver(); } public static void test() { synchronized (AR.class) { sleep(100); output("fun"); } } }
剩下的mysql和redis以及dubbo的Demo就不寫瞭,各位看官看著發揮即可,更多關於java性能測試框架的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- 手寫java性能測試框架的實現示例
- Java騷操作之CountDownLatch代碼詳解
- Java Spring攔截器案例詳解
- Java利用Redis實現高並發計數器的示例代碼
- 分析ZooKeeper分佈式鎖的實現