美文网首页我爱编程
Gatling 自动化压测&Jmeter

Gatling 自动化压测&Jmeter

作者: 燕京博士 | 来源:发表于2018-05-25 17:23 被阅读640次

    Gatling是一款基于Scala 开发的高性能服务器性能测试工具,它主要用于对服务器进行负载等测试;想使用Gatling进行压测的原因之一是想体验一下Scala编程的感觉,玩一下;第二,工作上也确实有这样的需求;

    压测工作简单来说就是利用压测应用,来测试一下服务器的响应性能参数;然后把这些工作全部自动化,集成到jenkins中来运行。

    整个工作的子任务分解可以由下图来表示:

    image

    压测使用的是一个常见的web应用,该web应用的具体使用的业务场景如下:

    image

    针对该应用的压测Scala源代码如下:
    文件名:performance.scala

    package performance
    
    import scala.concurrent.duration._
    
    import io.gatling.core.Predef._
    import io.gatling.http.Predef._
    import io.gatling.jdbc.Predef._
    
    class Performance extends Simulation {
    
      //用户名、餐馆ID 存储文件
      val user = csv("/root/.jenkins/workspace/testGatling/src/test/scala/data/user.csv").random
      val res = csv("/root/.jenkins/workspace/testGatling/src/test/scala/data/restaurant.csv").random
      val ip = csv("/root/.jenkins/workspace/testGatling/src/test/scala/data/ip.csv").random
    
      val httpProtocol = http
        .baseURL("http://${ip}:8180")
    
      val headers_0 = Map("Accept" -> "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
    
      val scn = scenario("Emenu")
        //打开订餐首页
        .exec(http("index")
        .get("/E_Menu/userlogin/login.jsp")
        .headers(headers_0))
    
        //登录
        .pause(1 second,20 second)
        .feed(user)
        .feed(res)
        .exec(http("login")
        .post("/E_Menu/userlogin/login")
        .headers(headers_0)
        .formParam("username", "${username}")
        .formParam("password", "${password}")
        )
    
        //选择餐馆
        .pause(1 second,20 second)
        .exec(http("menu")
        .get("/E_Menu/menu.action?res_num=${rest_id}")
        .headers(headers_0)
        )
        //点菜
        .pause(1 second,20 second)
        .exec(addCookie(Cookie("username", "${username}")))
        .exec(addCookie(Cookie("res_num", "${rest_id}")))
        .exec(addCookie(Cookie("food_num0", "105")))
        .exec(addCookie(Cookie("food_num1", "104")))
        .exec(addCookie(Cookie("food_num2", "104")))
        .exec(addCookie(Cookie("food_num3", "106")))
        .exec(addCookie(Cookie("total", "52")))
        .exec(http("list") //点完菜,开始订
        .get("/E_Menu/list.action")
        .headers(headers_0)
        )
        //下单
        .pause(1 second,20 second)
        .exec(http("order")
        .get("/E_Menu/order.action?people=5&time=2025-08-31")
        .headers(headers_0)
        )
    
        //    //回首页
        .pause(1 second,20 second)
        .exec(http("restaurant")
        .get("/E_Menu/restaurant.action")
        .headers(headers_0)
        )
    
        //用户信息
        .pause(1 second,20 second)
        .exec(http("userinfo")
        .get("/E_Menu/userinfo?username=${username}")
        .headers(headers_0)
        )
    
        //我的订单
        .pause(1 second,20 second)
        .exec(http("userorder")
        .get("/E_Menu/userorder.action?username=${username}")
        .headers(headers_0)
        )
    
        //退出
        .pause(1 second,20 second)
        .exec(http("exit")
        .get("/E_Menu/userlogin/login.jsp")
        .headers(headers_0))
    
      setUp(scn.inject(atOnceUsers(100))).protocols(httpProtocol)
    }
    
    

    写完scala代码,并且保证它在本地可以调试通过,下一步就是需要将代码集成进jenkins;
    先确保jenkins Gatling Plugin在jenkins上被安装;
    之后,写入用来执行的shell调控代码:

    #################### 环境准备 ####################
    pwd
    export IP="xx.xx.xx.xx"
    export user="root"
    export pwd="xxxxxxxxxx"
    export tomcat_path="/root/apache-tomcat-7.0.56/bin"
    export killtomcat="/root/killtomcat.sh"

    #################### 定义函数 ####################
    kill_tomcat7(){
    expect -c "
    spawn ssh $user@$IP sh $killtomcat;
    expect {
    yes/no { send yes\r;exp_continue }
    *password: { send $pwd\r }
    };
    expect eof;
    "
    }

    start_tomcat(){
    expect -c "
    spawn ssh $user@$IP sh $tomcat_path/$1;
    expect {
    yes/no { send yes\r;exp_continue }
    *password: { send $pwd\r }
    };
    expect eof;
    "
    }
    #################### tomcat压测 ####################

    停止tomcat

    kill_tomcat7

    启动tomcat

    start_tomcat startup_non.sh

    mvn gatling:execute -Dgatling.simulationClass=performance.Performance

    #################### 完毕 ####################

    参考截图如下:

    image

    在完成调试改bug工作之后,可以尝试运行一下,得到压测结果 Gatling report ,然后可以进行相关的数据分析,这里不再赘述。
    Jenkins 集成 Gatling Report参考截图如下:

    image

    11 个赞 关注 收藏

    所以具体实现过程我这里不多写了,我重点写下对生成的HTML测试报告进行优化

    如果按JMeter默认设置,生成报告如下:

    image

    从上图可以看出,结果信息比较简单,对于运行成功的case,还可以将就用着。但对于跑失败的case,就只有一行assert错误信息。(信息量太少了,比较难找到失败原因)

    优化大致过程

    1、修改jmeter.properties文件,打开一些输出内容开关(下图根据需要选择相关项,具体就不用多说了吧)

    image
    2、制定一份自己的输出模板。(不用默认的jmeter_home/extras/jmeter-results-detail-report.xsl模板,也可以网上自己找份。)
    3、最后执行,生成对应的HTML报告(一般我们都在linux环境 下运行,语句大致如下,其中my_project_template.xsl就上第2步说的定制模板,这个是网上找的一份。)

    xsltproc $jmeter_home/extras/my_project_template.xsl $my_project_workspace/result/jtl/$test_name/${test_name}.jtl > $my_project_workspace/result/html/$test_name/${test_name}.html

    最后报告如下:

    image

    image
    优化后的HTML报告,多了接口地址、接口参数、Headers信息(包括cookie、session),而且有返回结果。失败原因一目了然[图片上传失败...(image-9cb8f5-1527240193077)]

    最后附上我的jmeter文件样本:

    image

    什么是mock server

    mock:英文可以翻译为模仿的,mock server是我们用来解除依赖(耦合),假装实现的技术,比如说,前端需要使用某些api进行调试,但是服务端并没有开发完成这些api,那么前端的工作就被服务端阻塞了,那么就可以使用mock server假装实现这些api,能够返回特定的数据,帮助前端进行页面渲染,当然我们为了方便可以需要与服务端进行约定,约定接口的内容是什么。

    restful接口规范

    转接阮一峰老师的博客—RESTful API 设计指南:http://www.ruanyifeng.com/blog/2014/05/restful_api.html

    Moco-约定uri(一)

    moco工具是在github开源的一个项目,可以使用moco工具搭一个简单的mock server方便我们进行调试,github地址:https://github.com/dreamhead/moco,下载下来的是一个jar包,目前的版本是0.11.1,首先我们要编写一个config文件,把我们需要“模拟”的请求和响应写入这个配置文件,配置文件是json格式的,接下来我们写一个比较简单的请求,访问 localhost:12306/hello 接口,返回一个纯文本“moco”,moco工具约定了12306端口,不必纠结,就跟tomcat约定8080端口类似,config.json文件如下,而且json文件要与moco的jar包放在同一个文件夹下。比如博主的目录结构:

    G:\学习资料\mock\moco-runner-0.11.1-standalone.jar
    G:\学习资料\mock\config.json
    
    
    [
        {
            "request":
            {
                "uri":"/hello"
            },
            "response":
            {
                "text":"moco"
            }
        }
    ]
    

    配置文件比较简单,我们请求接口,返回一个纯文本,启动指令:

    >java -jar moco-runner-0.11.1-standalone.jar http -p 12306 -c config.json
    
    

    这里的http就是http协议, -p 12306 绑定端口号12306, -c config.json读config文件

    这里写图片描述

    看到以上的表现,就说明moco已经顺利启动了,我们访问localhost:12306/hello 看到结果如下就说明mock server顺利返回了我们约定的数据”moco”


    这里写图片描述

    Moco-约定uri(二)

    修改config文件如下,注意这里moco工具能实时监测到json配置文件的变化,并自行重启server


    这里写图片描述
    [
        {
            "request":
            {
                "uri":"/"
            },
            "response":
            {
                "text":"welcome to Moco"
            }
        },
        {
            "request":
            {
                "uri":"/hello"
            },
            "response":
            {
                "text":"moco"
            }
        }
    ]
    

    接下来分别访问localhost:12306和12306:12306/hello,结果如下:


    这里写图片描述 这里写图片描述

    Moco-约定get请求

    [
        {
            "request":
            {
                "method":"get",
                "uri":"/get"
            },
            "response":
            {
                "text":"moco get"
            }
        }
    ]
    

    Moco-约定post请求

    [
        {
            "request":
            {
                "method":"post",
                "uri":"/post"
            },
            "response":
            {
                "text":"moco post"
            }
        }
    ]
    

    Moco-约定请求参数

    [
        {
            "request":
            {
                "method":"get",
                "uri":"/get",
                "queries":
                {
                    "id":"12306",
                    "name":"moco"
                }
            },
            "response":
            {
                "text":"moco queries"
            }
        }
    ]
    
    这里写图片描述

    Moco-约定请求body必须为json格式

    [
        {
            "request":
            {
                "method":"post",
                "uri":"/post",
                "text":
                {
                    "json":"{\"id\":\"12306\",\"name\":\"moco\"}"
                }
            },
            "response":
            {
                "status":"200"
            }
        }
    ]
    

    Moco-约定请求头部

    [
        {
            "request":
            {
                "method":"post",
                "uri":"/post",
                "headers":
                {
                    "content-type":"application/json",
                    "Connection":"keep-alive",
                    "Content-Encoding":"gzip"
                }
            },
            "response":
            {
                "status":"200"
            }
        }
    ]
    

    Moco-约定返回内容

    前面已经看到了response的集中返回内容如text,和status,下面展示一下返回文件和设置文件格式等

    [
        {
            "request":
            {
                "method":"post",
                "uri":"/post",
            },
            "response":
            {
                "file":"data.js",
                "charset":"GBK",
                "version":"HTTP/1.0"
            }
        }
    ]
    

    Moco-约定返回状态码

    见上述的几个json,里面已经包含了返回状态码的使用方式

    Moco-在单元测试中的使用(以Python为例)

    [
        {
            "request":
            {
                "method":"get",
                "uri":"/api/hello"
            },
            "response":
            {
                "text":"hello Savitar!",
                "status":200
            }
        }
    ]
    

    这里模拟一个get请求,返回纯文本“hello Savitar!”和状态码200,先在浏览器访问localhost:12306/api/hello 结果如下图:


    这里写图片描述

    说明接口返回没问题,接下来使用Python requests+unittest写一个简单的接口测试用例

    
    #coding=utf-8
    '''
    @author=Savitar
    '''
    import unittest
    import requests
    
    class MocoTestApi(unittest.TestCase):
        def setUp(self):
            self.url = "http://localhost:12306"
        def test_moco_test_api(self):
            api = "/api/hello"
            url = self.url+api
            r = requests.get(url)
            self.assertEqual(r.status_code,200)
            self.assertEqual(r.text,"hello Savitar!")
        def tearDown(self):
            pass
    
    if __name__ == '__main__':
        unittest.main()
    

    相关文章

      网友评论

        本文标题:Gatling 自动化压测&Jmeter

        本文链接:https://www.haomeiwen.com/subject/jyyujftx.html