1.背景
最近寶路遇到個項目,在使用JMeter過程中引發了一些思考,寶路嘗試用各種方式去驗證,進而有了今天“JMeter原子性”相關主題。
2.目的
探究JMeter的事務的原子性
3.實戰
說道原子性,有的同學還一頭霧水。。。。啥是原子性?相信大家應該都接觸過數據庫,數據庫的事務就具有原子性(其餘的幾個特性本次不討論),寶路覺得這個原子性的概念,更像是從化學這麼學科中“偷”過來的。在化學中,原子是在化學反應不可再分的微粒。影射到事務,那就是一個或者多個操作步驟組成了一個原子單元,在事務執行過程中,如果其中任意一個操作步驟失敗,那麼整個事務即失敗。
說下本次JMeter的測試計劃,本次設計4個接口,“登錄”、“活動信息”、“查卡列表”、“查詢餘額”,只有登錄成功之後纔會觸發其餘交易。
腳本結構(全部交易默認成功):
腳本中sampler都用相同代碼來模擬,可以通過random參數控制返回內容,sleep參數控制交易耗時。
public class JMeterTransaction extends AbstractJavaSamplerClient { @Override public Arguments getDefaultParameters() { Arguments arguments = new Arguments(); arguments.addArgument("random","1"); arguments.addArgument("sleep","50"); return arguments; } @Override public SampleResult runTest(JavaSamplerContext arg0) { SampleResult result = new SampleResult(); result.sampleStart(); String random = arg0.getParameter("random"); long sleep = Long.valueOf(arg0.getParameter("sleep")); try { TimeUnit.MILLISECONDS.sleep(sleep); } catch (InterruptedException e) { e.printStackTrace(); } result.setSuccessful(true); result.setResponseCodeOK(); result.setResponseData(random,"UTF-8"); result.sampleEnd(); return result; } }
10併發執行1min結果:
嗯?各接口樣本數爲什麼不一樣?有點意思?理論上每個接口樣本數應該一致啊。。。。。
寶路調整了下事務控制器配置,勾選了Genernate parent sampler
10併發執行1min結果:
感覺目前沒啥問題,寶路將執行過程中的結果保存了,打開本次jtl結果:
嗯?如出一轍。。。從目前看與事務原子性,登錄成功多少筆,其餘的交易就應該成功多少筆。目前能看JMeter的事務控制不符合原子性,難道是我使用的姿勢有問題?
多次執行過,寶路發現個共性:同一個測試計劃的各接口的樣本總數是遞減的。所以寶路做了推測:在場景停止時(線程數從某個設定的值下降到0),比如某個線程在執行完“查詢卡列表”時,線程就被kill了,進而導致“查詢餘額”接口沒被執行。
寶路又做了驗證:
不勾選事務控制器Genernate parent sampler
勾選事務控制器Genernate parent sampler
清除結果後,使用聚合報告打開本次執行的jtl結果:
經過多次執行,發了個詭異現象:在不勾選Genernate parent sampler時,事務控制器統計的樣本數與最後一個接口統計的樣本數相同,在勾選Genernate parent sampler時,事務控制器統計的樣本數與首個接口統計的樣本數相同。
先不管這個是不是BUG,從事務角度看不滿足原子性。單純的事務控制器並不嚴格的具備原子性,這是目前看到的表像。
那麼怎麼解決呢?寶路之前寫過一篇關於JavaRequest探究的文章,感興趣的同學可以點擊看下。本文就不過多闡述了。直接看結果
清除聚合報告數據後,再打開本次執行的jtl結果文件
嗯,真香。目前覺得唯一的缺點就是沒有一個能監聽subResult的插件。相同的場景,都是基於java請求的編寫的底層腳本,唯一的區別就是組織方式不同。
其實你完全可以這樣看:把TestJavaLee中的main看成事務控制器,你細細品,再細細品。。。。
進而寶路得出:單純用事務控制器來組織多個sampler,嚴格來說不具有事務的原子性。
如果有不同想法或者看法,歡迎給寶路留言,最後附下TestJavaLee代碼:
public class TestJavaLee extends AbstractJavaSamplerClient { @Override public Arguments getDefaultParameters() { Arguments arguments = new Arguments(); arguments.addArgument("subResult","RAW_NO"); return arguments; } @Override public SampleResult runTest(JavaSamplerContext arg0) { SampleResult main = new SampleResult(); main.setSampleLabel("Main事務"); main.sampleStart(); SampleResult sample_a = new SampleResult(); sample_a.setSampleLabel("登錄"); sample_a.sampleStart(); try { TimeUnit.MILLISECONDS.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } sample_a.sampleEnd(); sample_a.setSuccessful(true); main.addSubResult(sample_a,false); SampleResult sample_b = new SampleResult(); sample_b.setSampleLabel("活動信息"); sample_b.sampleStart(); try { TimeUnit.MILLISECONDS.sleep(70); } catch (InterruptedException e) { e.printStackTrace(); } sample_b.sampleEnd(); sample_b.setSuccessful(true); main.addSubResult(sample_b,false); SampleResult sample_c = new SampleResult(); sample_c.setSampleLabel("查卡列表"); sample_c.sampleStart(); try { TimeUnit.MILLISECONDS.sleep(80); } catch (InterruptedException e) { e.printStackTrace(); } sample_c.sampleEnd(); sample_c.setSuccessful(true); main.addSubResult(sample_c,false); SampleResult sample_d = new SampleResult(); sample_d.setSampleLabel("查詢餘額"); sample_d.sampleStart(); try { TimeUnit.MILLISECONDS.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } sample_d.sampleEnd(); sample_d.setSuccessful(true); main.addSubResult(sample_d,false); main.setSuccessful(true); return main; } }