手寫(xiě)java性能測(cè)試框架的實(shí)現(xiàn)示例
引言
之前寫(xiě)過(guò)一個(gè)性能測(cè)試框架,只是針對(duì)單一的HTTP接口的測(cè)試,對(duì)于業(yè)務(wù)接口和非HTTP接口還無(wú)非適配,剛好前端時(shí)間工作中用到了,就更新了自己的測(cè)試框架,這次不再以請(qǐng)求為基礎(chǔ),而是以方法為基礎(chǔ),這樣就可以避免了單一性,有一個(gè)base類,然后其他的各種單一性請(qǐng)求在單獨(dú)寫(xiě)一個(gè)適配類就好了,如果只是臨時(shí)用,直接重新實(shí)現(xiàn)base即可。
代碼分享
package com.fun.frame.thead;
import com.fun.frame.SourceCode;
import com.fun.frame.excute.Concurrent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import static com.fun.utils.Time.getTimeStamp;
/**
* 多線程任務(wù)基類,可單獨(dú)使用
*/
public abstract class ThreadBase<T> extends SourceCode implements Runnable {
private static final Logger logger = LoggerFactory.getLogger(ThreadBase.class);
/**
* 任務(wù)請(qǐng)求執(zhí)行次數(shù)
*/
public int times;
/**
* 計(jì)數(shù)鎖
* <p>
* 會(huì)在concurrent類里面根據(jù)線程數(shù)自動(dòng)設(shè)定
* </p>
*/
CountDownLatch countDownLatch;
/**
* 用于設(shè)置訪問(wèn)資源
*/
public T t;
public ThreadBase(T t) {
this();
this.t = t;
}
public ThreadBase() {
super();
}
/**
* groovy無(wú)法直接訪問(wèn)t,所以寫(xiě)了這個(gè)方法
*
* @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("執(zhí)行次數(shù):{},總耗時(shí):{}", times, ee - ss);
Concurrent.allTimes.addAll(t);
} catch (Exception e) {
logger.warn("執(zhí)行任務(wù)失敗!", e);
} finally {
after();
if (countDownLatch != null)
countDownLatch.countDown();
}
}
/**
* 運(yùn)行待測(cè)方法的之前的準(zhǔn)備
*/
protected abstract void before();
/**
* 待測(cè)方法
*
* @throws Exception
*/
protected abstract void doing() throws Exception;
/**
* 運(yùn)行待測(cè)方法后的處理
*/
protected abstract void after();
public void setCountDownLatch(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
public void setTimes(int times) {
this.times = times;
}
}
基礎(chǔ)類實(shí)現(xiàn)
下面是幾個(gè)實(shí)現(xiàn)過(guò)的基礎(chǔ)類:
package com.fun.frame.thead;
import com.fun.httpclient.ClientManage;
import com.fun.httpclient.FanLibrary;
import com.fun.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請(qǐng)求多線程類
*/
public class RequestThread extends ThreadBase {
static Logger logger = LoggerFactory.getLogger(RequestThread.class);
/**
* 請(qǐng)求
*/
public HttpRequestBase request;
/**
* 單請(qǐng)求多線程多次任務(wù)構(gòu)造方法
*
* @param request 被執(zhí)行的請(qǐng)求
* @param times 每個(gè)線程運(yùn)行的次數(shù)
*/
public RequestThread(HttpRequestBase request, int times) {
this.request = request;
this.times = times;
}
@Override
public void before() {
request.setConfig(FanLibrary.requestConfig);
GCThread.starts();
}
@Override
protected void doing() throws Exception {
getResponse(request);
}
@Override
protected void after() {
GCThread.stop();
}
/**
* 多次執(zhí)行某個(gè)請(qǐng)求,但是不記錄日志,記錄方法用 loglong
* <p>此方法只適應(yīng)與單個(gè)請(qǐng)求的重復(fù)請(qǐng)求,對(duì)于有業(yè)務(wù)聯(lián)系的請(qǐng)求暫時(shí)不能適配</p>
*
* @param request 請(qǐng)求
* @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("響應(yīng)狀態(tài)碼:{},響應(yīng)內(nèi)容:{}", content, response.getStatusLine());
if (response != null) response.close();
}
}數(shù)據(jù)庫(kù)的實(shí)現(xiàn)
package com.fun.frame.thead;
import com.fun.interfaces.IMySqlBasic;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.SQLException;
/**
* 數(shù)據(jù)庫(kù)多線程類
*/
public class QuerySqlThread extends ThreadBase {
private static Logger logger = LoggerFactory.getLogger(QuerySqlThread.class);
String sql;
IMySqlBasic base;
public QuerySqlThread(IMySqlBasic base, String sql, int times) {
this.times = times;
this.sql = sql;
this.base = base;
}
@Override
public void before() {
base.getConnection();
}
@Override
protected void doing() throws SQLException {
base.excuteQuerySql(sql);
}
@Override
protected void after() {
base.mySqlOver();
}
}
concurrent類
package com.fun.frame.excute;
import com.fun.bean.PerformanceResultBean;
import com.fun.frame.Save;
import com.fun.frame.SourceCode;
import com.fun.frame.thead.ThreadBase;
import com.fun.profile.Constant;
import com.fun.utils.Time;
import com.fun.utils.WriteRead;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Concurrent {
private static Logger logger = LoggerFactory.getLogger(Concurrent.class);
/**
* 線程任務(wù)
*/
public ThreadBase thread;
public List<ThreadBase> threads;
public int num;
public static Vector<Long> allTimes = new Vector<>();
ExecutorService executorService;
CountDownLatch countDownLatch;
/**
* @param thread 線程任務(wù)
* @param num 線程數(shù)
*/
public Concurrent(ThreadBase thread, int num) {
this(num);
this.thread = thread;
}
/**
* @param threads 線程組
*/
public Concurrent(List<ThreadBase> threads) {
this(threads.size());
this.threads = threads;
}
public Concurrent(int num) {
this.num = num;
executorService = Executors.newFixedThreadPool(num);
countDownLatch = new CountDownLatch(num);
}
/**
* 執(zhí)行多線程任務(wù)
*/
public PerformanceResultBean start() {
long start = Time.getTimeStamp();
for (int i = 0; i < num; i++) {
ThreadBase thread = getThread(i);
thread.setCountDownLatch(countDownLatch);
executorService.execute(thread);
}
shutdownService(executorService, countDownLatch);
long end = Time.getTimeStamp();
logger.info("總計(jì)" + num + "個(gè)線程,共用時(shí):" + Time.getTimeDiffer(start, end) + "秒!");
return over();
}
private static void shutdownService(ExecutorService executorService, CountDownLatch countDownLatch) {
try {
countDownLatch.await();
executorService.shutdown();
} catch (InterruptedException e) {
logger.warn("線程池關(guān)閉失敗!", e);
}
}
private PerformanceResultBean over() {
Save.saveLongList(allTimes, num);
return countQPS(num);
}
ThreadBase getThread(int i) {
if (threads == null) return thread;
return threads.get(i);
}
/**
* 計(jì)算結(jié)果
* <p>此結(jié)果僅供參考</p>
*
* @param name 線程數(shù)
*/
public static PerformanceResultBean countQPS(int name) {
List<String> strings = WriteRead.readTxtFileByLine(Constant.LONG_Path + name + Constant.FILE_TYPE_LOG);
int size = strings.size();
int sum = 0;
for (int i = 0; i < size; i++) {
int time = SourceCode.changeStringToInt(strings.get(i));
sum += time;
}
double v = 1000.0 * size * name / sum;
PerformanceResultBean performanceResultBean = new PerformanceResultBean(name, size, sum / size, v);
performanceResultBean.print();
return performanceResultBean;
}
}
redis實(shí)現(xiàn)類缺失,因?yàn)闆](méi)有遇到需要單獨(dú)實(shí)現(xiàn)的需求。
關(guān)于用代碼還是用工具實(shí)現(xiàn)并發(fā),我個(gè)人看法所有所長(zhǎng),單究其根本,必然是代碼勝于工具,原因如下:門(mén)檻高,適應(yīng)性強(qiáng);貼近開(kāi)發(fā),利于調(diào)優(yōu)。
性能測(cè)試,并發(fā)只是開(kāi)始,只有一個(gè)好的開(kāi)始才能進(jìn)行性能數(shù)據(jù)分析,性能參數(shù)調(diào)優(yōu)。所以不必拘泥于到底使用哪個(gè)工具那種語(yǔ)言,據(jù)我經(jīng)驗(yàn)來(lái)說(shuō):基本的測(cè)試需求都是能滿足的,只是實(shí)現(xiàn)的代價(jià)不同。
groovy是一種基于JVM的動(dòng)態(tài)語(yǔ)言,我覺(jué)得最大的優(yōu)勢(shì)有兩點(diǎn)
- 第一:于java兼容性非常好,大部分時(shí)候吧groovy的文件后綴改成java直接可以用,反之亦然。java的絕大部分庫(kù),groovy都是可以直接拿來(lái)就用的。這還帶來(lái)了另外一個(gè)有點(diǎn),學(xué)習(xí)成本低,非常低,直接上手沒(méi)問(wèn)題,可以慢慢學(xué)習(xí)groovy不同于Java的語(yǔ)法;
- 第二:編譯器支持變得更好,現(xiàn)在用的intellij的ide,總體來(lái)說(shuō)已經(jīng)比較好的支持groovy語(yǔ)言了,寫(xiě)起代碼來(lái)也是比較順滑了,各種基于groovy的框架工具也比較溜,特別是Gradle構(gòu)建工具,比Maven爽很多。
以上就是java性能測(cè)試框架手寫(xiě)實(shí)現(xiàn)示例的詳細(xì)內(nèi)容,更多關(guān)于java 性能測(cè)試框架的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
springboot如何根據(jù)不同的日志級(jí)別顯示不同的顏色
這篇文章主要介紹了springboot如何根據(jù)不同的日志級(jí)別顯示不同的顏色問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08
java中char類型轉(zhuǎn)換成int類型的2種方法
這篇文章主要給大家介紹了關(guān)于java中char類型轉(zhuǎn)換成int類型的2種方法,因?yàn)閖ava是一門(mén)強(qiáng)類型語(yǔ)言,所以在數(shù)據(jù)運(yùn)算中會(huì)存在類型轉(zhuǎn)換,需要的朋友可以參考下2023-07-07
很多人竟然不知道Java線程池的創(chuàng)建方式有7種
本文主要介紹了很多人竟然不知道Java線程池的創(chuàng)建方式有7種,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07
IDEA使用SequenceDiagram插件繪制時(shí)序圖的方法
這篇文章主要介紹了IDEA使用SequenceDiagram插件繪制時(shí)序圖的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
java解決單緩沖生產(chǎn)者消費(fèi)者問(wèn)題示例
這篇文章主要介紹了java解單緩沖生產(chǎn)者消費(fèi)者問(wèn)題示例,需要的朋友可以參考下2014-04-04
SpringBoot中Date格式化處理的三種實(shí)現(xiàn)
Spring Boot作為一個(gè)簡(jiǎn)化Spring應(yīng)用開(kāi)發(fā)的框架,提供了多種處理日期格式化的方法,本文主要介紹了SpringBoot中Date格式化處理實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2024-03-03

