欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

spring boot2.0.4集成druid,用jmeter并发测试工具调用接口,druid查看监控的结果

程序员文章站 2023-11-22 23:43:16
一、项目介绍(本项目用的编程语言是jdk8,项目源码:https://github.com/zhzhair/spring-boot-druid.git) 1.引入pom依赖: org.springframework.boot< ......

一、项目介绍(本项目用的编程语言是jdk8,项目源码:https://github.com/zhzhair/spring-boot-druid.git)
  1.引入pom依赖:
  <dependencies>
    <dependency>
      <groupid>org.springframework.boot</groupid>
      <artifactid>spring-boot-starter-data-redis</artifactid>
    </dependency>
    <dependency>
      <groupid>org.springframework.boot</groupid>
      <artifactid>spring-boot-starter-web</artifactid>
    </dependency>
    <dependency>
      <groupid>org.mybatis.spring.boot</groupid>
      <artifactid>mybatis-spring-boot-starter</artifactid>
    <version>1.3.2</version>
    </dependency>
    <dependency>
      <groupid>mysql</groupid>
      <artifactid>mysql-connector-java</artifactid>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupid>org.springframework.boot</groupid>
      <artifactid>spring-boot-starter-test</artifactid>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupid>com.alibaba</groupid>
      <artifactid>druid-spring-boot-starter</artifactid>
      <version>1.1.14</version>
    </dependency>
  </dependencies>
  由引入的jar包可知,项目用mysql + mybatis + redis架构,数据库连接池用阿里的druid

  2.配置文件application.yml配置(配置mysql数据源、druid连接池及监控、redis):
  spring:
    datasource:
    driver-class-name: com.mysql.jdbc.driver
    url: "jdbc:mysql://127.0.0.1:3306/demo?useunicode=true&characterencoding=utf-8&usessl=false"
    username: root
    password: 123456
    type: com.alibaba.druid.pool.druiddatasource
    druid:
      max-active: 100
      min-idle: 10
      max-wait: 60000
      filter:
        stat:
          merge-sql: true
          slow-sql-millis: 200
      test-on-borrow: true
      validation-query: select 1
      use-global-data-source-stat: true
      # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
      # http://127.0.0.1:8080/druid2/index.html
      filters: stat,wall,slf4j
      pool-prepared-statements: true
      max-pool-prepared-statement-per-connection-size: 20
    redis:
      host: 127.0.0.1
      password:
      database: 0
      timeout: pt1m1s
      jedis:
        pool.max-active: 200
        pool.max-idle: 50
        pool.max-wait: pt-1s
        pool.min-idle: 10

  table-num: 64

  3.引入druid配置类(sql和uri监控访问地址:http://localhost:8080/druid/index.html,用户名和密码分别是admin和123456):

    package com.example.demo.config.druid;

    import com.alibaba.druid.support.http.statviewservlet;
    import com.alibaba.druid.support.http.webstatfilter;
    import org.springframework.boot.web.servlet.filterregistrationbean;
    import org.springframework.boot.web.servlet.servletregistrationbean;
    import org.springframework.context.annotation.bean;
    import org.springframework.context.annotation.configuration;

    @configuration
    public class druidconfiguration {
        @bean
        public servletregistrationbean druidstatviewservle2() {
            //org.springframework.boot.context.embedded.servletregistrationbean提供类的进行注册.
            servletregistrationbean<statviewservlet> servletregistrationbean = new servletregistrationbean<>(new statviewservlet(), "/druid2/*");
            //添加初始化参数:initparams
            servletregistrationbean.addurlmappings("/druid/*");
            //白名单:
    //        servletregistrationbean.addinitparameter("allow","192.168.1.106");
            //ip黑名单 (存在共同时,deny优先于allow) : 如果满足deny的话提示:sorry, you are not permitted to view this page.
            // servletregistrationbean.addinitparameter("deny", "192.168.1.73");
            //登录查看信息的账号密码.
            servletregistrationbean.addinitparameter("loginusername","admin");
            servletregistrationbean.addinitparameter("loginpassword","123456");
            //是否能够重置数据.
            servletregistrationbean.addinitparameter("resetenable","false");
            return servletregistrationbean;
        }
        
        @bean
        public filterregistrationbean druidstatfilter2(){
            filterregistrationbean<webstatfilter> filterregistrationbean = new filterregistrationbean<>(new webstatfilter());
            filterregistrationbean.setname("druidfilter2");
            //添加过滤规则.
            filterregistrationbean.addurlpatterns("/*");
            //添加不需要忽略的格式信息.
            filterregistrationbean.addinitparameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid2/*");
            return filterregistrationbean;
        }

    }

 

  4.在测试类创建表user_*和user_mobile_*:

    @runwith(springrunner.class)
    @springboottest
    public class demoapplicationtests {

        @resource
        private testuserservice userservice;
        @test
        public void contextloads() {
            userservice.droptables();
            userservice.createtables();
        }

    }
    
    @service
    public class testuserserviceimpl implements testuserservice {
        @resource
        private usermapper usermapper;//jdbc操作接口
        @value("${table-num}")
        private int tablenum;//分表的个数

        @override
        public void droptables() {
            intstream.range(0,tablenum).parallel().foreach(this::droptables);
        }

        private void droptables(int i){
            usermapper.droptable("user_" + i);
            usermapper.droptable("user_mobile_" + i);
        }

        @override
        public void createtables() {
            intstream.range(0,tablenum).parallel().foreach(this::createtables);
        }

        private void createtables(int i){
            string suffix = string.valueof(i);
            usermapper.createtableuser(suffix);
            usermapper.createtableusermobile(suffix);
        }
    }

 

  5.编写restful风格的接口(包括登录和注册):

    @restcontroller
    @requestmapping("test/user")
    public class testusercontroller extends basecontroller {
        @resource
        private testuserservice userservice;
        @resource
        private tokenmanager tokenmanager;//给登录用户生成token,并放到redis
        @requestmapping(value = "/loginbymobile", method = {requestmethod.get}, produces = {mediatype.application_json_value})
        public baseresponse<loginresponse> loginbymobile() {
            baseresponse<loginresponse> baseresponse = new baseresponse<>();
            integer userid = userservice.getuseridbymobile();
            if(userid != null){
                baseresponse.setcode(0);
                baseresponse.setmsg("手机号登录成功");
                string token = tokenmanager.generatetoken(userid);
                loginresponse loginresponse = new loginresponse();
                loginresponse.setuserid(userid);
                loginresponse.settoken(token);
                loginresponse.setexpire(system.currenttimemillis() + 3600 * 1000);
                baseresponse.setdata(loginresponse);
            }else{
                baseresponse.setcode(-3);
                baseresponse.setmsg("手机号未注册");
            }
            return baseresponse;
        }

        @requestmapping(value = "/register", method = {requestmethod.post}, produces = {mediatype.application_json_value})
        public baseresponse<user> register() {
            baseresponse<user> baseresponse = new baseresponse<>();
            integer userid = userservice.getuseridbymobile();
            if(userid == null){
                user user = userservice.register();
                baseresponse.setcode(0);
                baseresponse.setdata(user);
                baseresponse.setmsg("注册成功");
            }else{
                baseresponse.setcode(1);
                baseresponse.setmsg("手机号已被注册");
            }
            return baseresponse;
        }
    }
    
    @service
    public class testuserserviceimpl implements testuserservice {
        @resource
        private userservice userservice;
        @value("${table-num}")
        private int tablenum;//分表的个数

        @override
        public integer getuseridbymobile() {
            return userservice.getuseridbymobile(getmobilestr());
        }

        @override
        public user register() {
            userrequest userrequest = new userrequest();
            userrequest.setmobile(getmobilestr());
            userrequest.seticon("http://127.0.0.1/"+getmobilestr()+".jpg");
            int rand = new random().nextint(4);
            userrequest.setnickname(new string[]{"xiaoming","xiaohong","xiaoqiang","xiaoli"}[rand]);
            return userservice.register(userrequest);
        }

        /**
         * 模拟手机号
         */
        private string getmobilestr(){
            string[] strings = {"13","15","16","18"};
            string beginstring = strings[new random().nextint(4)];
            int a = new random().nextint(10_0000_0000);
            string endstring = string.valueof(a);
            int length = 9 - endstring.length();
            stringbuilder stringbuilder = new stringbuilder(beginstring);
            for (int i = 0; i < length; i++) {
                stringbuilder.append("0");
            }
            return stringbuilder.append(endstring).tostring();
        }
    }    
    
    @service
    public class userserviceimpl implements userservice {
        private final string user_id_inc = "user_id_inc";
        @resource
        private usermapper usermapper;//jdbc操作接口
        @resource(name = "stringredistemplate")
        private redistemplate<string, string> redistemplate;

        @value("${table-num}")
        private int tablenum;//分表的个数

        @transactional(isolation = isolation.repeatable_read)
        @override
        public user register(userrequest userrequest) {
            string usercode = redistemplate.opsforvalue().get(user_id_inc);
            integer userid;
            if(usercode == null){//如果redis的数据丢失,就找出最大的userid,并给user_id_inc赋值
                int temp = 0;
                for (int i = 0; i < tablenum; i++) {
                    integer maxuserid = usermapper.getmaxuserid(string.valueof(i));
                    if(maxuserid != null && temp < maxuserid){
                        temp = maxuserid;
                    }
                }
                userid = temp + 1;
                redistemplate.opsforvalue().set(user_id_inc,string.valueof(userid));
            }else{
                long num = redistemplate.opsforvalue().increment(user_id_inc,1);
                userid = integer.valueof(num + "");
            }
            user user = new user();
            user.setuserid(userid);
            user.setmobile(userrequest.getmobile());
            user.seticon(userrequest.geticon());
            user.setnickname(userrequest.getnickname());
            int rem = userid % tablenum;
            usermapper.insertuser(user,string.valueof(rem));
            int rem0 = math.abs(userrequest.getmobile().hashcode()) % tablenum;
            string mobile = userrequest.getmobile();
            usermapper.insertusermobile(mobile,userid,string.valueof(rem0));
            return user;
        }

        @override
        public integer getuseridbymobile(string mobile) {
            int rem = math.abs(mobile.hashcode()) % tablenum;
            return usermapper.getuserbymobile(mobile,string.valueof(rem));
        }

    }

 

二、用jmeter做并发测试(jmeter版本4.0):
  1.双击打开bin目录下的jmeter.bat文件,菜单选简体中文:options->choose language->chinese(simplified)。点文件夹图标可以选择已有的jmeter脚本。

  2.右键测试计划->添加->threads(users)->线程组,然后配置执行线程数、持续时间等信息。登录和注册我都建了单独的线程组,其中:登录的线程数36000,持续时间600秒;注册的线程数6000,持续时间600秒。
  3.右键测试计划->添加->监听器->(查看结果数和聚合报告等,用于分析并发测试结果)。
  4.分别右键选中登录和注册的线程组->添加->sampler->http请求,配置如下:
登录和注册的协议都填http,ip都填127.0.0.1,端口号都填8080。登录的方式选get,注册的方式选post。登录的路径填/test/user/loginbymobile,注册的路径填/test/user/register。
  5.点击打开聚合报告,启动项目,点击菜单栏绿色的三角形图标运行,观察聚合报告的结果如下图所示:

spring boot2.0.4集成druid,用jmeter并发测试工具调用接口,druid查看监控的结果

 

三、查看druid的sql监控和uri监控:
  jmeter运行时,访问http://localhost:8080/druid/index.html,sql监控和webui等监控结果如图所示:

spring boot2.0.4集成druid,用jmeter并发测试工具调用接口,druid查看监控的结果

spring boot2.0.4集成druid,用jmeter并发测试工具调用接口,druid查看监控的结果