本來想用LoaderRunner對Narya的網絡性能再做一次測試,LR的java vuser可以直接用java來編寫測試腳本,本來還是挺方便的,但是不知道爲什麼腳本一跑起來就報這麼個錯:
Error (-17998): Failed to get [param not passed in call] thread TLS entry.
實在是有點鬱悶的,網上搜索了很久也沒找到個所以然,只是有人模模糊糊的指出,有可能是LR的java vuser腳本不支持多線程。想來也是,LR是靠license的併發數來賣錢的,如果你一個vuser可以開多個線程,每個vuser開100個線程,那一個100人的license就可以當1萬人來用了,但我們narya的一個client本身就要開幾個線程,看來是無法用LR來測了。
好在開源的社區裏還有一個叫JMeter的性能測試工具。我們希望通過工具能夠更靈活的來配置我們的測試用例,包括併發用戶數,每個用戶發送的請求數,和每個請求之間的間隔時間等。JMeter內置了一種叫做java sampler的sampler,我們需要繼承AbstractJavaSamplerClient這個類,實現它的下面這個方法。
public SampleResult runTest(JavaSamplerContext javasamplercontext)
完成後將自定義的Sampler打包成jar放到jmeter_home/lib/ext目錄下
我們希望整個測試用例第一步登錄虛擬用戶,然後設置一個集合點,等所有的用戶都登錄完了進入第二步,高併發發送請求。等所有請求發送完後再登出。
對應的我們設置三個sampler,分別用來登錄、發送請求和登出。
LogonSampler,ActionSampler,LogoffSampler
因爲跑Sampler的線程和具體的測試代碼的線程並不是同一個,所以我們還需要設置一種同步方式,當具體的測試完成後能夠通知Sampler,這裏我們就用Java的wait()和notify()好了。
以登錄爲例,client跑在runQueue線程上,Sampler作爲一個listener,當client任務完成時回調listener方法改變success狀態並喚醒Sampler的線程。而Sampler線程啓動client線程就進入等待狀態。
TestClient tclient = new TestClient();
UsernamePasswordCreds creds =
new UsernamePasswordCreds(new Name(username), password);
BasicRunQueue rqueue = new BasicRunQueue();
Client client = new Client(creds, rqueue);
tclient.setClient(client);
tclient.addListener(this);
client.addClientObserver(tclient);
client.setServer("202.75.*.*", new int[]{45312});
client.logon();
// start up our event processing loop
rqueue.start();
synchronized(this) {
while (!success) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
client線程喚醒Sampler線程
if (listener != null) {
synchronized(listener) {
listener.actionPerformed();
listener.notify();
listener = null;
}
}
整個GUI看起來如下。其中,在線程組中可以設置需要運行的虛擬用戶數。
我們分別把三個Sampler放到三個Controller下,通過Controller來控制Sampler的運行。其中發送請求的Java Request Sampler放到一個Loop Controller下,這樣可以通過Controller來控制請求的次數。Synchronizing Timer可以作爲集合點來使用,高斯隨機定時器可以設定每個請求之間的間隔。
另外我們這裏還可以用參數的方式來設定每個虛擬用戶的登錄名和密碼,這個賬號數據可以放在一個外部的csv文件中,通過JMeter提供的CSV Data Set Config配置元件來配置。
在GUI中配置好文件名和變量信息,然後在Sampler的配置中就通過${...}來引用。
而GUI中的參數信息是在我們繼承的Sampler中配置
public Arguments getDefaultParameters(){
Arguments params = new Arguments();
params.addArgument("username", "test");
params.addArgument("password", "test");
return params;
}
這些都準備好之後,我們就可以在test plan中加入我們所需要獲得的反饋數據的種類了,只要添加自己感興趣的Listener就可以了。但是不要忘了在Sampler中對sampleResult的設置。
如果測試順利完成就把result設爲successful,否則一部分的listener無法正確顯示sampler是否執行成功。
public SampleResult runTest(JavaSamplerContext context) {
SampleResult result = new SampleResult();
result.sampleStart();
......
......
result.sampleEnd();
result.setSuccessful(true);
}
不過JMeter所有內置的Listener都只是對服務器返回的數據進行分析,它是無法直接獲得服務器的CPU,內存使用等數據的。如果要獲得服務器數據,簡單的辦法是直接登錄到服務器(Unix/Linux)上運行top,sar等命令。其次是通過jmeter sampler for rstatd的插件來完成。首先得在服務器上安裝並開啓rpc rstatd服務。rstatd進程可以蒐集內核提供的CPU,內存等的使用數據,然後客戶端可以通過rpc(遠程方法調用)來獲得這些數據。
可以用這個鏈接http://swirstatdsample.sourceforge.net/installation.html來獲得jmeter sampler for rstatd的插件,下載的jar包需要安裝到 jmeter_home/lib/ext下,另外還要修改一下ApacheJMeter_core.jar中的org/apache/jmeter/resources/messages.properties的一些數據,只要按照指示來做就可以了。
meter sampler for rstatd的插件中有一個rstatd:sampler,這個sampler通過rpc訪問服務器獲得數據,然後再配合定時器我們就可以以固定時間間隔獲得服務器的性能數據了。但要注意的是我們需要另外再開一個線程組,這樣纔可以在跑測試用例的同時來收集數據(在同一個線程組下所有的test element是順序執行的)。另外再添加一個rstatd:listener來顯示數據就可以了。
整個測試計劃這樣設置基本就可以了,最後需要注意的一點是JMeter本身在運行的時候會佔用一部分資源,這樣會一定程度的影響到測試的結果,特別是開啓大量的線程的情況下更是如此,這種情況下可以考慮直接用命令行來執行。
jmeter -n -t <testplan.jmx> -l <logfile.jtl>
testplan.jmx是測試計劃,logfile.jtl是測試結果的數據文件,可以測試結束後在GUI裏打開分析。