spring boot2.0.4集成druid,用jmeter并发测试工具调用接口,druid查看监控的结果
一、项目介绍(本项目用的编程语言是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.点击打开聚合报告,启动项目,点击菜单栏绿色的三角形图标运行,观察聚合报告的结果如下图所示:
三、查看druid的sql监控和uri监控:
jmeter运行时,访问http://localhost:8080/druid/index.html,sql监控和webui等监控结果如图所示:
上一篇: Linux 创建子进程执行任务的实现方法