【原】【亲测】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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容