在之前的鏈路壓測中文章中,我對單鏈路測試和鏈路參數流轉進行了一些實踐,具體的效果還不錯。產出如下:
無支路鏈路測試
但是在實際工作中,由於測試數據準備的誤差、測試環境數據變更、測試環境數據時效性等等問題,並不能保證每一條鏈路都能準備按照鏈路模型中所設計的次數和路徑運行。
還有一個比較重點的點:數據的錯誤,很容易導致鏈路實現代碼拋異常,這個問題在框架中doing()
方法運行中進行了異常的捕獲。
下面是固定請求次數
的模型中實現方法。
@Override
public void run() {
try {
before();
long ss = Time.getTimeStamp();
while (true) {
try {
threadmark = mark == null ? EMPTY : this.mark.mark(this);
long s = Time.getTimeStamp();
doing();
long et = Time.getTimeStamp();
executeNum++;
int diff =(int) (et - s);
costs.add(diff);
if (diff > HttpClientConstant.MAX_ACCEPT_TIME)
marks.add(diff + CONNECTOR + threadmark + CONNECTOR + Time.getNow());
if ((et - ss) > time || status() || ThreadBase.needAbort()) break;
} catch (Exception e) {
logger.warn("執行任務失敗!", e);
logger.warn("執行失敗對象的標記:{}", threadmark);
errorNum++;
}
}
long ee = Time.getTimeStamp();
logger.info("線程:{},執行次數:{}, 失敗次數: {},總耗時: {} s", threadName, executeNum, errorNum, (ee - ss) / 1000.0);
Concurrent.allTimes.addAll(costs);
Concurrent.requestMark.addAll(marks);
} catch (Exception e) {
logger.warn("執行任務失敗!", e);
} finally {
after();
}
}
但是問題來了,拋出異常之後,對於單次請求沒有太多影響,但是對鏈路測試影響比較大。很有可能導致下一次的鏈路執行會出現問題。比如單鏈路性能測試實踐中的案例,上一次鏈路測試並沒有將收藏智課正常取消,那麼下一次執行鏈路時候的推送課程可能就會少幾個(需求是推送過濾了老師原創和老師收藏的),那麼推薦列表中準備的測試數據很可能耗盡,導致某一次執行之後,就不會推薦任何課程了,最終測試會失敗。
還有有一種情況,就是上一次鏈路的失敗,加上每一個線程綁定了一個用戶,很可能導致下一個鏈路執行的失敗,例如:如何對修改密碼接口進行壓測、如何同時壓測創建和刪除接口文章中提到的,至少會將錯誤率提升一倍,影響統計結果。
增加鏈路中支路
針對上面提到的鏈路運行的問題,我想到一個解決思路:在鏈路節點執行之前或者之後進行一些簡單的邏輯判斷,將執行引入不同的之路,比如列表中已經沒有可以繼續執行的課程後,就結束改線程。由於測試中的線程數減少了,那麼繼續測試也就失去了意義,所以也就要結束整個性能測試。
當然邏輯控制
也可以用來針對不同接口的比例進行控制,不同於如何對N個接口按比例壓測中執行生成不同的HttpRequestBase
對象的多線程任務類,可以通過在鏈路中邏輯控制器
來靜態或者動態調整接口請求次數甚至是次序。這個以後有機會再分享實踐經驗。
我用單鏈路性能測試實踐中的案例進行修改,對幾個可能出現的問題點進行邏輯處理。
@Override
protected void doing() throws Exception {
mirro.getKList()
def klist = mirro.getKList()
def karray = klist.getJSONArray("data")
if (karray.size() < 3) return ThreadBase.stop()
def kss = []
karray.each {
JSONObject parse = JSON.parse(JSON.toJSONString(it))
def level = parse.getIntValue("node_level")
def type = parse.getIntValue("ktype")
def id = parse.getIntValue("id")
kss << new K(id, type, level)
}
K ks = kss.get(0)
K ks2 = kss.get(1)
K ks3 = kss.get(3)
clazz.recommend(ks3.id, ks3.type, ks3.level)
clazz.recommend(ks2.id, ks2.type, ks2.level)
JSONObject response = clazz.recommend(ks.id, ks.type, ks.level)
def minis = []
int i = 0
def array = response.getJSONArray("data")
if (karray.size() < 2) return
array.each {
if (i++ < 2) {
JSONObject parse = JSON.parse(JSON.toJSONString(it))
int value = parse.getIntValue("minicourse_id")
clazz.collect(value, ks.type, ks.id)
}
}
mirro.getMiniCourseListV3(ks.id, ks.type, 0, ks.level)
def res = mirro.getMiniCourseListV3(ks.id, ks.type, 0, ks.level)
res.getJSONObject("data").getJSONArray("minicourse_list").each {
def ss = JSON.parseObject(JSON.toJSONString(it))
def mid = ss.getIntValue("minicourse_id")
clazz.unCollect(mid, ks.type, ks.id)
}
}
這裏我增加了兩個分支控制器:1、知識點列表爲空,直接結束線程if (karray.size() < 3) return ThreadBase.stop()
;2、推薦列表長度小於2,退出當前鏈路的單次執行,不影響接下來的執行,if (karray.size() < 2) return
。
兩個例子有點牽強,各位看官將就看看,有了更多的實踐,我會及時寫出來。
同步結束性能測試
在鏈路測試中支路的問題中,還有一個同步結束測試的問題,因爲一旦存在支路,勢必會產生鏈路執行時間的差異,在固定請求次數的模型中,等待所有線程正常運行結束再去統計測試結果,誤差會變大,所以要在最快運行的線程結束之後,就要停掉整個測試,統計數據。
具體實現方法如下:
@Override
protected void after() {
super.after()
ThreadBase.stop()
}
重寫after()
方法,在最先完成的線程執行結束,即刻執行stop()
方法,修改表示符號,結束所有線程。
-
由於工作原因,文章更新頻率不如以往,各位萬萬不可取關啊。
FunTester,騰訊雲年度作者、Boss直聘簽約作者,非著名測試開發er, 歡迎關注。
本文分享自微信公衆號 - FunTester(NuclearTester)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。