1. 程式人生 > >如何為Apache JMeter開發外掛(一)

如何為Apache JMeter開發外掛(一)

為什麼選擇使用JMeter

當被問到這個問題的時候,也許你會在腦海裡產生很多的理由,比如:

  • Apache基金會下的開源專案,沒有版權問題;
  • 為數不多的還在持續更新的開源效能自動化測試工具;
  • 支援協議豐富,是商用測試工具最佳替代品;
  • 有專門的外掛專案做支撐,使得你在實踐中有更多的選擇,比如http://jmeter-plugins.org/就提供了很多優秀的外掛為你在使用JMeter執行測試時,可以選擇更多的元件來制定測試計劃,完成測試過程,監控測試資料。

而我的回答是:

關鍵在於不要簡單地把JMeter理解為一個單純的效能測試工具,而應該意識到它還是一個優秀的框架,這甚至成為我選擇它的一個最根本理由,在這裡所有的元件都可以通過自由編寫外掛的方式進行新增和完善,對於一個測試工程師來說為JMeter編寫外掛式元件其樂無窮!

JMeter基本元件型別及實現方法

對於JMeter的基本元件,我們可以將其簡單的劃分為兩大類:

  • 一類是具備GUI的元件,即可以通過JMeter圖形管理控制器在測試計劃Tree中進行新增的元件,主要包括ThreadGroup(執行緒組)、Config(配置元件)、Timer(定時器)、Modifier(前置處理器)、Extractor(後置處理器)、Controller(邏輯控制器)、Sampler(測試抽樣器)、Assertion(斷言)和Listener(監聽器);
  • 另一類是非GUI元件,這類元件典型的代表是Function(函式)和某些子測試抽樣器,如JavaSamplerClient。

對於元件一般有兩種實現方法:

  1. GUI與邏輯控制分離:GUI部分通過繼承各種元件GUI抽象類,邏輯控制部分通過繼承元件邏輯抽象類和實現各種介面方式從而實現不同元件的內部邏輯控制;
  2. GUI與邏輯控制不分離:與分離方法的區別在於不單獨實現GUI部分,在邏輯控制部分通過實現TestBean介面方法從而實現對GUI介面的配置。

JMeter外掛式元件實現細節概述

TestElement是所有元件的最基本單元,元件類都是TestElement類的子類,JMeter定義了上一章節所介紹的幾種元件模型,並對其規範了各自所需發揮的作用。

(1)GUI部分的實現
GUI部分的實現我們可以在JMeter實現主類org.apache.jmeter.JMeter中發現端倪,該類實際實現了JMeterPlugin介面中的getIconMappings()方法來對映元件所對應的GUI圖示,併為對映關係定義了一個二維陣列,如下程式碼:

private static final String[][] DEFAULT_ICONS = {
        { "org.apache.jmeter.control.gui.TestPlanGui", "org/apache/jmeter/images/beaker.gif" },
        { "org.apache.jmeter.timers.gui.AbstractTimerGui", "org/apache/jmeter/images/timer.gif" },
        { "org.apache.jmeter.threads.gui.ThreadGroupGui", "org/apache/jmeter/images/thread.gif" },
        { "org.apache.jmeter.visualizers.gui.AbstractListenerGui", "org/apache/jmeter/images/meter.png" },
        { "org.apache.jmeter.config.gui.AbstractConfigGui", "org/apache/jmeter/images/testtubes.png" },
        { "org.apache.jmeter.processor.gui.AbstractPreProcessorGui", "org/apache/jmeter/images/leafnode.gif"},
        { "org.apache.jmeter.processor.gui.AbstractPostProcessorGui", "org/apache/jmeter/images/leafnodeflip.gif"},
        { "org.apache.jmeter.control.gui.AbstractControllerGui", "org/apache/jmeter/images/knob.gif" },
        { "org.apache.jmeter.control.gui.WorkBenchGui", "org/apache/jmeter/images/clipboard.gif" },
        { "org.apache.jmeter.samplers.gui.AbstractSamplerGui", "org/apache/jmeter/images/pipet.png" },
        { "org.apache.jmeter.assertions.gui.AbstractAssertionGui", "org/apache/jmeter/images/question.gif"}
};

可以看到只要是外掛類的GUI部分繼承了以上陣列中的GUI類,JMeter框架便會自動將其對映為所對應的元件型別和圖示。

(2)邏輯控制部分的實現細節,我們根據元件類別進行一一介紹:

這裡寫圖片描述ThreadGroup(執行緒組)元件

ThreadGroup(執行緒組)元件繼承AbstractThreadGroup抽象類,通過重寫各類控制方法,如void scheduleThread(JMeterThread thread) 、stopThread(String threadName, boolean now) 、threadFinished(JMeterThread thread)等,來達到控制和協調各執行緒(虛擬使用者)的行為,執行緒組是構建一個性能測試模型的最基本元件。

這裡寫圖片描述Config(配置元件)元件

Config(配置元件)元件相對其他元件比較特殊,通過繼承ConfigTestElement類或只需要GUI部分的實現即可完成本體任務,而對於一個需要配置的元件類則需要實現ConfigMergabilityIndicator介面的public boolean applies(ConfigTestElement configElement)方法,用來指明哪些Config元件可以用來對其進行配置,這裡參考TCPSampler的原始碼如下:

private static final Set<String> APPLIABLE_CONFIG_CLASSES = new HashSet<String>(
            Arrays.asList(new String[]{
                    "org.apache.jmeter.config.gui.LoginConfigGui",
                    "org.apache.jmeter.protocol.tcp.config.gui.TCPConfigGui",
                    "org.apache.jmeter.config.gui.SimpleConfigGui"}));

    @Override
    public boolean applies(ConfigTestElement configElement) {
        String guiClass = configElement.getProperty(TestElement.GUI_CLASS).getStringValue();
        return APPLIABLE_CONFIG_CLASSES.contains(guiClass);
}

以上程式碼指明LoginConfigGui、SimpleConfigGui和TCPConfigGui這三個配置元件可以對TCPSampler元件進行配置。

這裡寫圖片描述Timer(定時器)元件

Timer(定時器)元件通過繼承AbstractTestElement抽象類,實現Timer介面的delay()方法來實現對時間的控制,主要的控制內容如下:

  • 控制執行緒延時,即用來模仿思考時間(ThinkTime)或鍵盤時間(KeyTime);
  • 控制執行緒行為,如SyncTimer(同步計時器),就是內部利用CyclicBarrier來控制阻塞和釋放全部執行執行緒的邏輯行為,從而達到“集合點”的目的。

這裡寫圖片描述Modifier(前置處理器)元件

Modifier(前置處理器)元件通過繼承AbstractTestElement抽象類,實現PreProcessor介面的process ()方法控制邏輯,常常需要對執行緒上下文中的當前Sampler和前一個SampleResult進行識別和判斷,以做出正確的處理,一般的行為是通過取出SampleResult的某些值或直接在當前Sampler啟動sample方法之前對其某些屬性進行修飾。

這裡寫圖片描述Extractor(後置處理器)元件

Extractor(後置處理器)元件通過繼承AbstractTestElement抽象類,實現PostProcessor介面的process ()方法控制邏輯,常常需要對執行緒上下文中的前一個SampleResult進行識別和判斷,以做出正確的處理。

這裡寫圖片描述Controller(控制器)元件

Controller(控制器)元件通過繼承GenericController類,通過重寫Sampler next()、void setDone(boolean done)、int getIterCount()、void reInitialize()等方法來控制Sampler的測試行為。

這裡寫圖片描述Sampler(測試抽樣器)元件

Sampler(測試抽樣器)元件繼承AbstractSampler抽象類,通過重寫SampleResult sample(Entry e)方法,實現測試過程以及測試結果的採集功能。

這裡寫圖片描述Assertion(斷言)元件

Assertion(斷言)元件通過繼承AbstractTestElement抽象類,實現Assertion介面的getResult(SampleResult result)方法對結果內容進行判斷,從而實現斷言方法,用於對Sampler元件所產生的抽樣採集結果內容進行斷言。

這裡寫圖片描述Listener(監聽器)主要有兩種方案:

  • 直接繼承AbstractTestElement,實現sampleListener或Visualizer等介面方法
  • 實現ResultCollector和Runnable等介面方法

我們可以從實際用途上將其分為兩大類Report (報告)和Vizualizers(監視器)。
Report (報告)繼承AbstractListenerElement抽象類,通過實現sampleOccurred(SampleEvent e)方法,對所有采集事件中所產生的SampleResult進行處理,從而生成報告;
Vizualizers(監視器)主要用於特定的監控任務,比如監控系統資源利用率的元件,與Report的區別在於Vizualizers必須繼承一個 ResultCollector類,並在收集器中通過開啟額外執行緒方式完成自定義的資料採集。

上面還介紹了諸如Function(函式)這一類非GUI元件,這類元件的實現比較簡單,而且功能比較單一,只需要繼承相應的抽象類。

(3)一些TestElement需要實現的主要介面說明

為了實現更多的特性,元件在必要時還需要實現一些主要的介面和方法,下面舉例說明:

NoThreadClone介面:
This class is not cloned per thread, so this is shared,可以理解為一旦實現了NoThreadClone介面,這個TestElement便不會線上程組下的每個執行緒中建立,而是一個全域性化的元件,因此,無法使用getThreadContext()方法。反之,我們可以發現JMeter實際是通過TestElement的clone()方法為執行緒組下的每個執行緒拷貝建立屬於各自的執行緒上下文內的TestElement,A new instance is created for each thread group, and the clone() method is then called to create copies for each thread in a thread group.如果想要對某些共享資源進行同步操作,需要參考如下方法:

private transient Object lock = new Object();//鎖物件不需要進行序列化,因為分散式在不同主機記憶體中的鎖物件不必保持一致
@Override
public Object clone() {
    Clazz clazz = (Clazz) super.clone();
    clazz.lock = lock; //保證所有克隆物件共享同一個鎖物件
    return clazz;
}

注:這是種顯式的共享物件方法,由於Java在克隆類物件時,預設是一種“淺克隆”方式,因此,不顯式的共享上述鎖物件,該鎖物件也是預設共享的。

LoopIterationListener介面:
將通過實現 iterationStart(LoopIterationEvent event)方法,控制對每次發生迭代事件時所需要實現的邏輯。

Serializable介面:
為了實現在分散式測試模型中保持一些配置和物件的一致性,就需要實現Serializable介面,通過序列化方式保持不同主機物件屬性的一致性。

TestStateListener介面:
將通過實現testStarted(String string)和testEnded(String string)方法,控制對測試狀態變化事件時所需要實現的邏輯。

另外,在實際外掛的編寫過程中還會包括如Remoteable、Interruptible、ThreadListener等介面的應用,會在後面的具體章節進行詳細介紹和應用。

元件即外掛,只需一步插入JMeter框架

將編寫好的外掛式元件插入JMeter框架非常簡單,只需要將元件整體打包為jar包,並將其拷貝到$JMETER_HOME/ lib/ext路徑下即可使用!