效能測試概念再理解——集合點
什麼是集合點
集合點可以簡單得理解為一種控制虛擬使用者行為的機制,該機制可以達到在一定時間範圍內將一定數量的虛擬使用者阻擋在一個操作行為點前的位置進行互相等待,在條件(達到虛擬使用者數量或超時)到達後喚醒全部等待中的虛擬使用者,從而達到使得一定數量的虛擬使用者可以同時進入下一個操作行為點的目的。
往往其使用初衷是為了實現最大意義上的併發來考察系統應對此種極端情況的表現。
JMeter中所提供的SyncTimer(同步計時器),就是內部利用CyclicBarrier來控制阻塞和釋放全部執行執行緒的邏輯行為,從而達到“集合點”的目的,參考以下程式碼:
private static final class BarrierWrapper implements Cloneable {
private CyclicBarrier barrier;
public BarrierWrapper() {
this.barrier = null;
}
public BarrierWrapper(int parties) {
this.barrier = new CyclicBarrier(parties);
}
public synchronized void setup(int parties) {
if(this.barrier== null) {
this.barrier = new CyclicBarrier(parties);
}
}
public int await() throws InterruptedException, BrokenBarrierException{
return barrier.await();
}
public int await (long timeout, TimeUnit timeUnit) throws InterruptedException, BrokenBarrierException, TimeoutException {
return barrier.await(timeout, timeUnit);
}
public void reset() {
barrier.reset();
}
@Override
protected Object clone() {
BarrierWrapper barrierWrapper= null;
try {
barrierWrapper = (BarrierWrapper) super.clone();
barrierWrapper.barrier = this.barrier;
} catch (CloneNotSupportedException e) {
//Cannot happen
}
return barrierWrapper;
}
}
利用內部靜態類BarrierWrapper包裹實現了CyclicBarrier類的全部方法,並確保了使用延時載入方式初始化一個真正的CyclicBarrier物件。
Timer實際上通過delay()方法實現控制,參考以下程式碼:
@Override
public long delay() {
if(getGroupSize()>=0) {
int arrival = 0;
try {
if(timeoutInMs==0) {
arrival = this.barrier.await();
} else if(timeoutInMs > 0){
arrival = this.barrier.await(timeoutInMs, TimeUnit.MILLISECONDS);
} else {
throw new IllegalArgumentException("Negative value for timeout:"+timeoutInMs+" in Synchronizing Timer "+getName());
}
} catch (InterruptedException e) {
return 0;
} catch (BrokenBarrierException e) {
return 0;
} catch (TimeoutException e) {
LOGGER.warn("SyncTimer "+ getName() + " timeouted waiting for users after:"+getTimeoutInMs()+"ms");
return 0;
} finally {
if(arrival == 0) {
barrier.reset();
}
}
}
return 0;
}
每個執行緒到達這裡都會執行this.barrier.await()來等待其他執行緒,直到到達你所配置的等待使用者值或者超時值(timeoutInMs)時完成釋放,這時arrival會等於0,從而使用barrier.reset()對barrier進行重置,為下一批進入的執行緒設定阻塞。
達到增壓的目的了嗎?
集合點真的可以達到對系統增加壓力的效果嗎?
事實是不一定的(這種情況一般出現在ThinkTime和KeyTime被置0的前提下),這和你對其的使用方法和系統本身的構造息息相關,遇到的真實情況可能是在某個操作行為點上加了集合點後,整個事務效能變得更好了,這聽起來有點不可思議,但你可以嘗試分析一下原因:
(1)當用戶同時起步向前的時候,系統可以更加輕鬆的應對
混亂場面下的使用者可能會各自處在不同的業務操作點下,有些在查詢,有些在插入,有些在更新,這會對資料庫的事務完整性控制提出要求,資料庫會通過讀提交、可重讀或同步序列的方式,利用表級鎖、行級鎖來避免髒讀和幻讀的出現從而避免破壞ACID,在這種情況下必定會產生大量的鎖競爭,對效能會產生一定損耗。
但在使用者同步調的進行業務操作時,往往會大幅度的降低這種鎖競爭的局面,另外,這種情況下更加適合於快取這種利器效果的發揮,這對系統來說會省下了不少力氣。
(2)集合點的出現會明顯緩解服務端所直接承受的壓力
單獨業務點上雖然看似集合後壓力增大了,但是這個要分析具體情況,首先,在釋放阻塞之前,使用者會處於互相等待的狀態中,往往會有一些表現不盡如人意的使用者拖了後腿,於是工具會陷入漫長的阻塞狀態中,對服務端的壓力逐漸減小,服務端的壓力得到明顯緩解,從而得到了喘息和休整的機會。
(3)測試工具統計結果中會加入等待時所產生的資料
使用者都在集合點互相等待會導致TPS直線下降,原因是測試工具一般會把這段時間所產生的資料也算到統計中,這就會造成在最終使用這份工具生成測試結果時的一些困擾,需要利用原始值進一步加工。
正確使用集合點
首先,不是特別建議你在測試場景的設計中加入集合點,實際上在忽略ThinkTime和KeyTime的情況下,已經可以叫併發測試了,而且效果也完全達到了預期。
如果想使用集合點,你要有十分充足的需求作為導向。另外,建議將大事務拆解為更加短小精悍的幾個小事務,並且通過分組執行調節這些事務之間的關係,比如一個組可以跑整個大事務或一些組按事務混合比跑其中幾個小事務串兒作為持續不斷的壓力背景,在這種情況下精選幾個關鍵性業務下的事務組合放入集合點,一個一個的在壓力背景環境下下執行測試,重點關注它們的真正並發表現。
一些其他用途
(1)處理業務流或資料流上下游關係
當業務流或資料流繼承關係要求耦合性非常高時,比如下游所需要處理的業務或資料必須依賴於上游所有或一定量使用者完成相應業務處理或產生資料的基礎上,可以利用集合點達到正確處理這類關係的效果。
(2)流控
當配合一些邏輯判斷指令碼或工具元件時,通過判斷是否在一些特殊條件滿足的情況下啟動集合點,可以達到使用者流量控制的效果。