基于Jmeter的性能測試框架搭建

引言

談到性能測試咕幻,部分公司連專門用于性能測試的環(huán)境都沒有渔伯,更別提性能測試框架/平臺了。下面肄程,筆者就“基于Jmeter的性能測試框架搭建”這個(gè)話題锣吼,談?wù)勛约旱囊恍┫敕ā?/p>

工具

Jmeter
Influxdb
Grafana
Telegraf
Jenkins
Ant
Gitlab

理念

  • 測試人員只需專注腳本編寫及性能結(jié)果分析。腳本提交Gitlab后自動觸發(fā)構(gòu)建蓝厌,性能結(jié)果實(shí)時(shí)展現(xiàn)玄叠。
  • 性能測試腳本統(tǒng)一管理。


    性能測試框架

實(shí)現(xiàn)方法

  • 依賴Jmeter的Backend Listener監(jiān)聽器拓提,采集tps,響應(yīng)時(shí)間诸典,cpu,內(nèi)存等信息至Influxdb時(shí)序數(shù)據(jù)庫,然后再通過Grafana展現(xiàn)性能結(jié)果。
  • 依賴Jenkins的webhook插件監(jiān)聽push事件狐粱,即push腳本至gitlab則觸發(fā)Ant構(gòu)建舀寓。
一、腳本上傳小工具開發(fā)

壓測小工具

開始構(gòu)建

為了簡便測試人員操作肌蜻,特開發(fā)此壓測小工具互墓,實(shí)現(xiàn)功能如下:

  • 上傳腳本前,初始化本地git倉庫蒋搜。
  • 克隆git倉庫篡撵。
  • 根據(jù)上傳的腳本修改build.xml文件。
  • push腳本和build.xml文件豆挽。

使用JGit訪問Gitlab育谬,dom4j處理xml文件。pom.xml配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.tool</groupId>
    <artifactId>performanceTestTool</artifactId>
    <version>1.0-SNAPSHOT</version>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>6</source>
                    <target>6</target>
                </configuration>
            </plugin>
        </plugins>
    </build>


    <repositories>
        <repository>
            <id>jgit-repository</id>
            <url>https://repo.eclipse.org/content/groups/releases/</url>
        </repository>
    </repositories>

    <!-- Core Library -->
    <dependencies>
        <dependency>
            <groupId>org.eclipse.jgit</groupId>
            <artifactId>org.eclipse.jgit</artifactId>
            <version>4.11.0.201803080745-r</version>
        </dependency>

        <!-- Smart HTTP Servlet -->
        <dependency>
            <groupId>org.eclipse.jgit</groupId>
            <artifactId>org.eclipse.jgit.http.server</artifactId>
            <version>4.11.0.201803080745-r</version>
        </dependency>

        <!-- AWT UI Helpers -->
        <dependency>
            <groupId>org.eclipse.jgit</groupId>
            <artifactId>org.eclipse.jgit.ui</artifactId>
            <version>4.11.0.201803080745-r</version>
        </dependency>

        <!-- JUnit Test Support -->
        <dependency>
            <groupId>org.eclipse.jgit</groupId>
            <artifactId>org.eclipse.jgit.junit</artifactId>
            <version>4.11.0.201803080745-r</version>
        </dependency>

        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
    </dependencies>

</project>

clone項(xiàng)目至本地倉庫:

    public static void cloneProject() throws Exception {

        //每次clone前先初始化
        Util.deletefile(localProject);

        File file = new File(localProject);
        try {
            //克隆代碼庫命令
            CloneCommand cloneCommand = Git.cloneRepository();
            cloneCommand.setURI(remoteRepoURI) //設(shè)置遠(yuǎn)程URI
                    .setBranch("master") //設(shè)置clone下來的分支
                    .setDirectory(file) //設(shè)置下載存放路徑
                    .setCredentialsProvider(usernamePasswordCredentialsProvider)
                    .call();
        } catch (GitAPIException e) {
            e.printStackTrace();
        }
    }

pull操作:

    public static void pullFiles() throws IOException, GitAPIException {

        //git倉庫地址
        Git git = new Git(new FileRepository(localProject+"/.git"));
        git.pull().setRemoteBranchName("master").
                setCredentialsProvider(usernamePasswordCredentialsProvider).call();
    }

push操作:

    public static void pushFiles(String filePath,String commitMess) throws IOException, GitAPIException {

        File fileFrom = new File(filePath);
        File fileTemp = new File(localProject);
        File fileTo = new File(fileTemp.getAbsolutePath()+"/"+fileFrom.getName());
        fileTo.createNewFile();

        Util.copyFiles(fileFrom,fileTo);  //拷貝腳本文件至git本地倉庫

        Repository rep = new FileRepository(localProject+"\\.git");
        Git git1 = new Git(rep);
        git1.add().addFilepattern(fileFrom.getName()).call();  //此處必須使用本地倉庫中需推送的文件名
        git1.add().addFilepattern("build.xml").call();         //push修改后的build文件
        //提交
        git1.commit().setMessage(commitMess).call();
        git1.push().setCredentialsProvider(usernamePasswordCredentialsProvider).call();
    }

build.xml文件配置:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project basedir="." default="run" name="Ant">
    <tstamp>
        <format pattern="yyyyMMddhhmm" property="time"/>
    </tstamp>
    <!-- jmeter路徑-->  
    <property name="jmeter.home" value="D:\tools\PerformanceTesting\Jmeter_influxdb_grafana\apache-jmeter-3.1"/>
    <!-- jmeter jtl測試報(bào)告生成路徑--> 
    <property name="jmeter.result.jtl.dir" value="D:\tools\PerformanceTesting\Jmeter_influxdb_grafana\apache-jmeter-3.1\TestCase\report\jtl"/>
    <!-- jmeter html測試報(bào)告生成路徑-->
    <property name="jmeter.result.html.dir" value="D:\tools\PerformanceTesting\Jmeter_influxdb_grafana\apache-jmeter-3.1\TestCase\report\html"/>
    <!-- 參數(shù)化-->  
    <property name="ReportName" value="TestReport"/>
    <property name="jmeter.result.jtlName" value="${jmeter.result.jtl.dir}/${ReportName}${time}.jtl"/>
    <property name="jmeter.result.htmlName" value="${jmeter.result.html.dir}/${ReportName}${time}.html"/>
    
    <target name="run">
        <antcall target="test"/>
        <!--性能腳本構(gòu)建時(shí)帮哈,生成報(bào)告時(shí)間太長膛檀,注釋掉 -->
        <!-- antcall target="report"/ -->
    </target>
    
    <target name="test">
        <taskdef classname="org.programmerplanet.ant.taskdefs.jmeter.JMeterTask" name="jmeter"/>
        <jmeter jmeterhome="${jmeter.home}" resultlog="${jmeter.result.jtlName}">
             <!-- 構(gòu)建路徑,與jenkins上工作空間配置保持一致 -->
            <testplans dir="D:\tools\PerformanceTesting\Jmeter_influxdb_grafana\apache-jmeter-3.1\TestCase\jenkins_jobworkspace" includes="測試.jmx"/>
            <property name="jmeter.save.saveservice.output_format" value="xml"/>

        </jmeter>
    </target>
        
    <target name="report">
        <xslt in="${jmeter.result.jtlName}" out="${jmeter.result.htmlName}" style="${jmeter.home}/extras/jmeter.results.shanhe.me.xsl"/>
                <!-- 測試報(bào)告--> 
        <copy todir="${jmeter.result.html.dir}">
            <fileset dir="${jmeter.home}/extras">
                <include name="collapse.png"/>
                <include name="expand.png"/>
            </fileset>
        </copy>
    </target>
</project>

通過分析上面的build.xml文件娘侍,發(fā)現(xiàn)構(gòu)建腳本由includes的值來定義咖刃,如果值為“*.jmx”,則會構(gòu)建dir目錄下所有的jmx文件憾筏。由于我們只需構(gòu)建上傳的腳本嚎杨,那有必要修改build文件,使includes的值等于上傳的腳本名稱氧腰。

<testplans dir="D:\tools\PerformanceTesting\Jmeter_influxdb_grafana\apache-jmeter-3.1\TestCase\jenkins_jobworkspace" includes="測試.jmx"/>

修改build文件的includes值:

    public static void modifyBuild(String filePath,String modifyName) throws ParserConfigurationException, IOException, SAXException {

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();

        DocumentBuilder db = dbf.newDocumentBuilder();
        Document doc = db.parse(new File(filePath)); // 讀取xml文件

        NodeList targetList = doc.getElementsByTagName("testplans");  //獲取testplans節(jié)點(diǎn)

        for (int i = 0; i < targetList.getLength(); i++) {
            Node case_node = targetList.item(i); // 第一個(gè)caseNo節(jié)點(diǎn)

            Element elem0 = (Element) case_node; // caseNo節(jié)點(diǎn)對象
            String target_name = elem0.getAttribute("includes");
            System.out.println("修改前includes = " + target_name); // 節(jié)點(diǎn)屬性

            elem0.setAttribute("includes",modifyName);
            String target_name1 = elem0.getAttribute("includes");
            System.out.println("修改后includes = " + target_name1); // 節(jié)點(diǎn)屬性
        }
        saveXml(filePath, doc);
    }

至此枫浙,第一階段的工作已完成。當(dāng)然古拴,你也可以通過Git bash來push腳本觸發(fā)構(gòu)建箩帚,但是你得另外想辦法來控制腳本的構(gòu)建,因?yàn)锳nt是根據(jù)build.xml文件來指定構(gòu)建哪些腳本的斤富。

二、配置Jenkins,Gitlab
  • 安裝jenkins插件锻狗。
    Ant Plugin
    Git plugin
    GitLab Plugin
    Gitlab Hook Plugin
    可以在線安裝满力,也可以下載本地安裝,下載地址
    轻纪。
  • jenkins-系統(tǒng)配置-全局工具配置油额。
    配置jdk,git,ant。


    jdk

    git&ant
  • jenkins-項(xiàng)目-配置刻帚。
    自定義空間與build.xml設(shè)置的構(gòu)建路徑保持一致潦嘶,jenkins構(gòu)建時(shí),會把git倉庫pull到該路徑下崇众。
<testplans dir="D:\tools\PerformanceTesting\Jmeter_influxdb_grafana\apache-jmeter-3.1\TestCase\jenkins_jobworkspace" includes="測試.jmx"/>
自定義空間

git源碼管理

注意下圖的Targets需同build.xml文件一致掂僵。

    <target name="run">
        <antcall target="test"/>
        <!--性能腳本構(gòu)建時(shí)航厚,生成報(bào)告時(shí)間太長,注釋掉 -->
        <!-- antcall target="report"/ -->
    </target>
構(gòu)建

由于依賴webhook來監(jiān)聽push事件觸發(fā)構(gòu)建锰蓬,拷貝下圖的URL幔睬,并在“高級”選項(xiàng)中生成“Secret token”,后續(xù)在gitlab添加webhook芹扭。


構(gòu)建觸發(fā)器

構(gòu)建觸發(fā)器
  • gitlab-webhook配置麻顶。
    由于只需監(jiān)聽push事件,所有下圖只勾選了Push events“”舱卡。添加webhook后辅肾,點(diǎn)擊“Test”進(jìn)行測試,如果返回200/201轮锥,則表明webhook配置成功矫钓。


    webhook

    webhook-test

    如果不想使用Secret token配置webhook,也可按以下方式來配置:
    打開jenkins的“設(shè)置”頁面交胚,找到API Token份汗,然后在gitlab上添加webhook url即可,結(jié)構(gòu)如下圖2所示(UserId:APIToken@jenkins構(gòu)建器url)蝴簇。


    API Token

    webhook.png
三杯活、配置Influxdb,Grafana,Telegraf

Influxdb+Grafana+Telegraf在筆者的另一篇文章<Jmeter排憂解難—性能測試監(jiān)控>有提及,百度也大把文章熬词,此處不再詳述旁钧。

四、編寫測試腳本

使用jmeter編寫一個(gè)簡單的測試腳本來進(jìn)行測試互拾,主要依賴Backend Listener監(jiān)聽器來集成influxdb歪今。


測試腳本

測試腳本
五、性能結(jié)果分析

運(yùn)行“一颜矿、腳本上傳小工具開發(fā)”提及的壓測小工具寄猩,就可以對性能結(jié)果做實(shí)時(shí)監(jiān)控了。下圖1僅對tps和響應(yīng)時(shí)間做采集骑疆,由于筆者未在被測服務(wù)器上安裝Telegraf田篇,所以cpu,內(nèi)存等數(shù)據(jù)暫不采集,有興趣的童鞋可以自行探索箍铭。更詳細(xì)的監(jiān)控結(jié)果如圖2所示泊柬。


性能測試結(jié)果

性能測試結(jié)果
六、構(gòu)建日志

登錄jenkins诈火,可以查看詳細(xì)的構(gòu)建日志兽赁。


Jenkins構(gòu)建日志

后續(xù)改進(jìn)

其實(shí)以上框架還存在不少待改進(jìn)之處,筆者后續(xù)再逐步解決。

  • Grafana性能圖表實(shí)時(shí)展現(xiàn)刀崖,測試過程中需實(shí)時(shí)截圖形成測試報(bào)告惊科,不夠人性化。
    解決方案:自動生成測試報(bào)告并郵件通知蒲跨。
  • Grafana性能圖表需測試人員實(shí)時(shí)監(jiān)控译断,人力成本較高。
    解決方案:自動生成測試報(bào)告并郵件通知或悲。
  • 多腳本構(gòu)建的話孙咪,無法區(qū)分Grafana展現(xiàn)的性能圖表對應(yīng)哪個(gè)腳本。
    解決方案:傳參區(qū)分腳本巡语,并生成每個(gè)接口對應(yīng)的測試報(bào)告翎蹈。
  • 如果考慮持續(xù)監(jiān)控,可加入預(yù)警功能男公。
    解決方案:依賴Grafana的預(yù)警功能荤堪。
  • 未能自動生成測試報(bào)告。
    解決方案:自動生成測試報(bào)告并郵件通知枢赔。
  • 需登錄jenkins停止腳本構(gòu)建澄阳,操作不夠便利。
    解決方案:前端增加停止構(gòu)建操作踏拜。
  • 每次只能提交一個(gè)腳本進(jìn)行構(gòu)建碎赢。
    解決方案:支持批量構(gòu)建。

《基于Jmeter的性能測試框架搭建》改進(jìn)一
《基于Jmeter的性能測試框架搭建》改進(jìn)二

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子溯革,更是在濱河造成了極大的恐慌,老刑警劉巖顿肺,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)拷窜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來涧黄,“玉大人篮昧,你說我怎么就攤上這事」” “怎么了恋谭?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵糠睡,是天一觀的道長挽鞠。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么信认? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任材义,我火速辦了婚禮,結(jié)果婚禮上嫁赏,老公的妹妹穿的比我還像新娘其掂。我一直安慰自己,他們只是感情好潦蝇,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布款熬。 她就那樣靜靜地躺著,像睡著了一般攘乒。 火紅的嫁衣襯著肌膚如雪贤牛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天则酝,我揣著相機(jī)與錄音殉簸,去河邊找鬼。 笑死沽讹,一個(gè)胖子當(dāng)著我的面吹牛般卑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播爽雄,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蝠检,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了盲链?” 一聲冷哼從身側(cè)響起蝇率,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎刽沾,沒想到半個(gè)月后本慕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡侧漓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年锅尘,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片布蔗。...
    茶點(diǎn)故事閱讀 39,902評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡藤违,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出纵揍,到底是詐尸還是另有隱情顿乒,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布泽谨,位于F島的核電站璧榄,受9級特大地震影響特漩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜骨杂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一涂身、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧搓蚪,春花似錦蛤售、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至雳灾,卻和暖如春搜骡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背佑女。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工记靡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人团驱。 一個(gè)月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓摸吠,卻偏偏與公主長得像,于是被迫代替她去往敵國和親嚎花。 傳聞我的和親對象是個(gè)殘疾皇子寸痢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容