【原】【親測】Jmeter 自定義sampler壓測elk 寫入數據到redis

前提概要:本文主要講述如何開發一個Jmeter的自定義sampler,目的是根據自己的業務場景可以自定義壓測規則,本文的初衷是為了對elk做壓力測試,而作者的elk的logstash數據來源是redis,所以本文所寫的sampler主要其實是壓測數據寫入redis。

主要參考了以下博文:

http://www.nttpc.co.jp/technology/jmeter.html

https://testerhome.com/topics/4522

http://blog.csdn.net/bobchao0730/article/details/51352768

JMeter插件實現步驟1 - 準備開發環境

JMeter插件實現是標準的Java代碼,新建一個Maven項目。我們的項目中需要引用到JMeter本身提供的一些庫,包括ApacheJMeter_core和ApacheJmeter_java,并且對JMeter的版本依賴是3.1。另外,還需要對redis的類庫也需要引入,pom.xml中依賴部分的代碼如下。

<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <jmeter-version>3.1</jmeter-version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.apache.jmeter</groupId>
            <artifactId>ApacheJMeter_core</artifactId>
            <version>${jmeter-version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.jmeter</groupId>
            <artifactId>ApacheJMeter_java</artifactId>
            <version>${jmeter-version}</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.7</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.7</version>
        </dependency>

        <!-- log4j2-鏈接數據庫 -->
        <dependency>
            <groupId>commons-pool</groupId>
            <artifactId>commons-pool</artifactId>
            <version>1.6</version>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>
    </dependencies>
    <build>
        <finalName>${project.artifactId}</finalName>
        <defaultGoal>install</defaultGoal>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.5.1</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
                <executions>
                    <execution>
                        <id>assemble-all</id>
                        <phase>package</phase>
                        <goals>
                            <goal>single</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

工程創建完畢之后,開始編寫代碼來實現插件。
注意:這里用到了一個maven插件,這個maven插件的作用是打包maven項目的時候,連該項目依賴的所有jar包一起打包進來,因為jmter的插件打包成功之后,這些依賴都需要,如果不打包進來的話,實際上插件是會無法使用的。

JMeter插件實現步驟2 - 自定義界面

JMeter的插件機制會在$JMETER_HOME/lib/ext目錄下去動態加載符合指定條件的JAR包,并在JMeter中顯示出來。比如要擴展UI的話,擴展的Java類的包名必須是”.gui.”,同樣的擴展函數的Java類的包名必須是”.function.”.
新建一個類,com.weds.test.elk.gui.ELKSamplerGui,并指定其父類為AbstractSamplerGui。
該類需要實現以下的功能:

1)界面布局與控件。JMeter的界面是標準的Swing,所以里面的控件和布局都是標準的Swing寫法。

2)界面與Sampler之間的數據交換。Sampler在JMeter中繼承的是TestElement,用戶輸入的數據都是保存在Sampler中的,因此可以認為這個是界面的模型。界面與模型(Sampler)之間的數據交換需要實現父類的下面幾個方法,

public void configure(TestElement el)

該方法用于把Sampler中的數據加載到界面中。在實現自己的邏輯之前,先調用一下父類的方法super.configure(el),這樣可以確保框架自動為你加載一些缺省數據,比如Sampler的名字。

public void modifyTestElement(TestElement e)

這個方法用于把界面的數據移到Sampler中,剛好與上面的方法相反。在調用自己的實現方法之前,請先調用一下super.configureTestElement(e),這個會幫助移到一些缺省的數據。

public TestElement createTestElement()

該方法創建一個新的Sampler,然后將界面中的數據設置到這個新的Sampler實例中。

public void clearGui()

該方法會在reset新界面的時候調用,這里可以填入界面控件中需要顯示的一些缺省的值。

完整代碼如下:

package com.weds.test.elk.gui;

import com.weds.test.elk.ELKSampler;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.gui.util.VerticalPanel;
import org.apache.jmeter.samplers.gui.AbstractSamplerGui;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jorphan.gui.JLabeledTextArea;
import org.apache.jorphan.gui.JLabeledTextField;

import javax.swing.*;
import java.awt.*;

/**
 * 日志壓測工具
 * <p>
 *    壓測,收集數據寫入到redis
 * </p>
 */
public class ELKSamplerGui extends AbstractSamplerGui {

    //swing 頁面主面板
    private JPanel mainPanel;

    private JLabeledTextField redisIP = new JLabeledTextField("redis服務器ip地址");
    private JLabeledTextField redisPort = new JLabeledTextField("redis服務器");
    private JLabeledTextField redisList = new JLabeledTextField("投遞目標");
    private JLabeledTextArea redisMessage = new JLabeledTextArea("日志消息");

    public ELKSamplerGui() {
        setLayout(new BorderLayout(0, 5));
        setBorder(makeBorder());
        add(makeTitlePanel(), BorderLayout.NORTH); // Add the standard title
        mainPanel = new VerticalPanel();
        init();
        add(mainPanel, BorderLayout.CENTER);      //添加面板
    }

    /**
     * 初始化屏幕布局
     */
    protected final void init() {
        mainPanel.add(redisIP);
        mainPanel.add(redisPort);
        mainPanel.add(redisList);
        mainPanel.add(redisMessage);
        redisMessage.setPreferredSize(new Dimension(100, 25));
    }

    /**
     * 該方法用于把Sampler中的數據加載到界面中。
     * 在實現自己的邏輯之前,先調用一下父類的方法super.configure(el),
     * 這樣可以確保框架自動為你加載一些缺省數據,比如Sampler的名字。
     *
     * @param element
     */
    @Override
    public void configure(TestElement element) {
        super.configure(element);
        if (!(element instanceof ELKSampler)) {
            return;
        }
        ELKSampler sampler = (ELKSampler) element;
        redisIP.setText(sampler.getRedisIP());
        redisPort.setText(sampler.getRedisPort());
        redisList.setText(sampler.getRedisList());
        redisMessage.setText(sampler.getRedisMessage());
    }

    @Override
    public String getLabelResource() {
        return null;
    }

    public String getStaticLabel() {
        return "Elk Sampler";
    }

    @Override
    public TestElement createTestElement() {
        ELKSampler sampler = new ELKSampler();
        modifyTestElement(sampler);
        return sampler;
    }

    /**
     * 這個方法用于把界面的數據移到Sampler中,剛好與上面的方法相反。
     * 在調用自己的實現方法之前,
     * 請先先調用一下super.configureTestElement(e),這個會幫助移掉一些缺省的數據。
     *
     * @return
     */
    @Override
    public void modifyTestElement(TestElement te) {
        ELKSampler sampler = (ELKSampler) te;
        sampler.clear();
        super.configureTestElement(sampler);
        sampler.setRedisIP(redisIP.getText());
        sampler.setRedisPort(redisPort.getText());
        sampler.setRedisList(redisList.getText());
        sampler.setRedisMessage(redisMessage.getText());
    }

    /**
     * 該方法會在reset新界面的時候調用,這里可以填入界面控件中需要顯示的一些缺省的值。
     */
    @Override
    public void clearGui() {
        super.clearGui();
    }
}

JMeter插件實現步驟3 - 自定義redis Sampler
新建一個Sampler,com.weds.test.elk.ELKSampler,并指定其父類為AbstractSampler。該類需要實現以下的功能:

1)增加一些getter/setter方法,這些方法用于與UI之間的數據交換,這些數據在用戶保存/打開腳本的時候將被自動序列化/反序列化。

2)實現sample方法:

public SampleResult sample(Entry entry)

該方法是JMeter實現對目標系統發起請求實際工作的地方。主要的工作是記錄請求處理時間,對返回結果進行處理和判斷,并根據處理結果返回SampleResult,該SampleResult中需要指定返回的內容是否成功,以及消息等。
完整代碼如下:

package com.weds.test.elk;

import org.apache.jmeter.samplers.AbstractSampler;
import org.apache.jmeter.samplers.Entry;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jorphan.logging.LoggingManager;
import org.apache.log.Logger;
import redis.clients.jedis.Jedis;


public class ELKSampler extends AbstractSampler {

    //輸出到日志文件
    static org.apache.logging.log4j.Logger logger = org.apache.logging.log4j.LogManager.getLogger(ELKSampler.class.getName());

    //控制臺輸出類
    private static final Logger log = LoggingManager.getLoggerForClass();
    private transient Jedis jedis;

    private final static String redisIP = "AMQPPublisher.RedisIP";
    private final static String redisPort = "AMQPPublisher.RedisPort";
    private final static String redisList = "AMQPPublisher.RedisList";
    private final static String redisMessage = "AMQPPublisher.RedisMessage";


    public String getRedisIP() {
        return getPropertyAsString(redisIP);
    }

    public void setRedisIP(String content) {
        super.setProperty(redisIP, content);
    }

    public String getRedisPort() {
        if (getPropertyAsInt(redisPort) < 1) {
            return "6379";
        }
        return getPropertyAsString(redisPort);
    }

    public void setRedisPort(String content) {
        super.setProperty(redisPort, content);
    }

    public String getRedisList() {
        return getPropertyAsString(redisList);
    }

    public void setRedisList(String content) {
        super.setProperty(redisList, content);
    }

    public String getRedisMessage() {
        return getPropertyAsString(redisMessage);
    }

    public void setRedisMessage(String content) {
        super.setProperty(redisMessage, content);
    }

    public ELKSampler() {
    }

    @Override
    public SampleResult sample(Entry entry) {

        SampleResult result = new SampleResult();
        result.setSampleLabel(getName());
        result.setSuccessful(false);
        result.setResponseCode("500");
        result.setSampleLabel(this.getName());
        result.sampleStart(); // Start timing
        try {
            log.info("-------------------------發送消息到redis中:" + getRedisMessage() + "-------------------------");
            logger.error("-------------------------" + getRedisMessage() + "-------------------------");
            jedis = new Jedis(getRedisIP(), Integer.parseInt(getRedisPort()));
            jedis.rpush(getRedisList(), getRedisMessage()); // Sampler data)
            result.setSamplerData(getRedisMessage());
            result.setResponseData(getRedisMessage(), null);
            result.setDataType(SampleResult.TEXT);
            result.setResponseCodeOK();
            result.setResponseMessage("OK");
            result.setSuccessful(true);
        } catch (Exception ex) {
            log.debug(ex.getMessage(), ex);
            result.setResponseCode("000");
            result.setResponseMessage(ex.toString());
        } finally {
            result.sampleEnd(); // End timimg
        }

        return result;
    }
}

JMeter插件實現步驟4 - 打包、部署
JMeter插件實現步驟5- 測試,查看測試結果,大功告成。

1.png
2.png

Paste_Image.png
Paste_Image.png
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容