zoukankan      html  css  js  c++  java
  • spring boot2.0.4集成druid,用jmeter并发测试工具调用接口,druid查看监控的结果

    一、项目介绍(本项目用的编程语言是jdk8,项目源码: https://github.com/zhzhair/mybatis-druid-spring-boot.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.点击打开聚合报告,启动项目,点击菜单栏绿色的三角形图标运行,观察聚合报告的结果如下图所示:

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

  • 相关阅读:
    【Qt】Qt5.12连接MySQl5.7(亲自测试成功)
    【Qt】Qt5.12编译MySQl5.7驱动(亲自测试成功)
    【网络安全】十三款流行无线黑客工具介绍
    微信公众号教程(15)公司通讯录开发 下
    微信公众号教程(14)公司通讯录开发 中
    微信公众号教程(13)公司通讯录开发 上
    微信公众号教程(12)公众账号接收非文字消息 下
    微信公众号教程(11)公众账号接收非文字消息 上
    微信公众号教程(10)公众账号自定义回复功能
    微信公众号教程(9)公众账号发送欢迎图文消息
  • 原文地址:https://www.cnblogs.com/zhzhair-coding/p/10657971.html
Copyright © 2011-2022 走看看