win下压测:
1.1)先下载jmeter
1.2)解压之后,找到bin目录下的jmeter.bat,运行(jmeter就是用java写的图形界面的应用程序)。
1.3)jmeter非常人性化,选项—选择语言,可以选择中文简体。
Options->Choose Language->Chinese(Simplified)
1.4)现在,我们先找个页面压测一下,压测前,我们先把程序都启动:
我们先来压测商品列表:/goods/to_list
右击测试计划,依次点击:添加---线程(用户)---线程组
在线程组上右击---添加---配置元件---HTTP请求默认值,配置了这个之后,其他的元件中就不用重复在配置了。
在线程组上右击---添加---取样器---HTTP请求
在线程组上右击---添加---监听器---聚合报告,这个显示的比较粗略,详细的可以添加图形结果等元件。
点击运行按钮,点击YES,保存执行计划,暂时保存到桌面。
这就是结果,主要看这个Error和Throughput就行。
现在将线程数设置为1000,再来查看结果:
比原来大了好多,并发量在1000的时候,370左右的吞吐量(Throughput:吞吐量——默认情况下表示每秒完成的请求数)。
现在将线程数设置为5000,再来查看结果:
好吧,出现错误了,看一下后台,并没有报错,这就是一个最基本的压测。
我们来看一下代码:这里其实什么也没做,只是查询了数据库,所以说瓶颈还是在数据库。
@GetMapping("/to_list")
public String list(Model model, SecKillUser user) {
model.addAttribute("user", user);
//查询商品列表
List goodsList = goodsService.listGoodsVo();
model.addAttribute("goodsList", goodsList);
return "goods_list";
}
如何证明瓶颈在数据库上?这里我就换成虚拟机中的数据库,使用top命令来查看一下:
这是还没有进行测试时的情况,下面我们把并发量设置为1000,循环10次,进行压测:
这里可以很明显的看到,当压测开始时,我们的mysql进程直接排到前面来,这就说明了瓶颈是在数据库。
我是在自己电脑上装的虚拟机,只是简单做个测试,演示一下压测的效果。
这次我们压测另一个接口:因为我们说,我们系统很多时候都要获取用户这个对象,我们来看一下,假如说我们什么也不做,单纯的来获取这个对象看一看我们系统的QPS能够到达多少。为此新建一个UserController进行测试
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/info")
@ResponseBody
public Result info(Model model, SecKillUser user) {
return Result.success(user);
}
}
如何进行压测???
这里我们使用token来传递参数,实际也能用cookie来传递参数,先禁用商品列表,然后新建一个HTTP请求,改名为:获取用户信息,填入相关信息,那么这个token的值从哪里获取?
我们就用这个token就行。其他的跟原来一样,并发量1000,循环10次:
这个的吞吐量超过1000,虽然出现了错误,但后台并没有报错(可能是电脑资源被占用的太多了,反应不过来吧,仅供学习演示)。
算了,调大redis和mysql的参数试试
# spring.datasource.maxActive=2
spring.datasource.maxActive=1000
# spring.datasource.initialSize=1
spring.datasource.initialSize=100
spring.datasource.maxWait=60000
# spring.datasource.minIdle=1
spring.datasource.minIdle=500
# redis.poolMaxTotal=10
redis.poolMaxTotal=1000
# redis.poolMaxIdle=10
redis.poolMaxIdle=500
# 秒
# redis.poolMaxWait=3
redis.poolMaxWait=500
然而还是有错,但后台没报错,跟之前一样......
关于这个Error,先不追究了,先来看看这个1000+的吞吐量,那段程序做了什么,可以有1000+的吞吐量?
对用户信息的处理在UserArgumentResolver这个类,其他的不占用时间,只有getByToken这个方法占用时间多一些。
这里就是从redis中读取了一个值,再写入一个cookie,因为是对redis的操作,使用的是缓存,所以吞吐量更高点。而之前的商品列表,涉及到数据库的操作,所以吞吐量比较低些。
当然,这样测试也不太合适,毕竟只用一个用户,进行多次测试。现在尝试多用户的测试。
右击线程组---添加---配置元件---CSV数据文件设置
新建一个文件,随便起个名config,内容是:userId,userToken
引用文件中的变量:
再测试一下(其实这里是看不出来什么的......,要不就在程序中添加输出语句,看看效果......)
因为有时候我们就像直接在我们的服务器上做压测,因为我们的服务器大多数情况下都是linux的,没有图形界面的,我们来看一下,在命令行下面我们是如何使用的。
这里做测试时,为了方便,还是用jar包。后面会简单说一下打war包。
先把jdk、redis都安装配置好,然后上传jmeter、jar包和需要的配置文件
我先上传了jmeter和jar包,使用nohup和java来运行jar包,nohup的作用就是:忽略输入,并把输出追加到"nohup.out"。
我们看一下这个文件的内容:实际上就是IDEA控制台输出的内容
打开浏览器,这里使用的是虚拟机的地址来访问:
程序正常运行。
我们还是先来压测商品列表,这次设置并发量5000,循环10次
将这个文件另存为goods_list,上传到服务器
从图中可以看到负载11.51,最高达到21+,而处理器只有两个,严重超载,当然,这并不是恒定不变的,有一段时间是两个java线程使用了更多的cpu,一个是secondkill,一个是jmeter本身。这个测试只是为了练习,不要太在意结果...
我们把测试结果下载下来,导入到jmeter中,看看内容:
在并发量5000,循环10次的情况下,Error为0,QPS:495.1,那我们再跑一次看看:
这次更高些(第一次算是预热)966。我们就把这次的数据作为一个参考,优化完之后再次进行压测,作比较。
下面我们来压测/miaosha/do_miaosha,这个算是我们的核心代码了,这里先改一下数据库时间,使其可以秒杀,库存都设置为10。
因为“秒杀”这个方法需要user和goodsId,这个user我们使用token来模拟。我们先来生成5000个用户和token,这个生成的程序就不贴代码了,就是做循环......
要说的是:在生成数据时,调用doLogin有个小问题
doLogin本身返回的是Boolean类型,但是我们想要它返回一个token,是String类型,所以这里先改成String类型
//生成用户信息时用
@PostMapping("/do_login")
@ResponseBody
public Result doLogin(HttpServletResponse response, @Valid LoginVo loginVo){
log.info(loginVo.toString());
//登陆
String token = userService.login(response, loginVo);
//如果出现异常,则直接抛出了,所有直接返回true就行
return Result.success(token);
}
public String login(HttpServletResponse response, LoginVo loginVo) {
//public boolean login(HttpServletResponse response, LoginVo loginVo) {
......
return token;
//return true;
}
改完后,启动工程,再启动生成用户信息的程序,将用户信息插入数据库的同时,保存一份token用于压测。
生成的token,从0到4999:
数据库也插入了5000条数据(还包括之前用来测试的那条数据):
再来设置JMeter脚本:
上传文件到服务器:
修改脚本文件中,引用tokens.txt的路径:
再进行压测,下载结果,导入JMeter查看:
这次负载达到40多了...
这个秒杀的压测,只有176(Throughput),确实挺低的,不过也是意料之中,因为秒杀这个接口做的事情比较多:
先是判断库存,然后是判断是否重复秒杀,最后是减库存下订单,都是与数据库进行交互,吞吐量低也正常。
我们跟之前一样,再重新压测一次:
并发量5000,循环10次,吞吐量555.
好了,测试先到此为止。
这是虚拟机的配置,其他的还有centos7.4
到目前为止,我们的程序都是打jar包,以main函数方式运行,但是实际项目中,我们很有可能是要把程序打成war包,放到我们的tomcat服务器下面,这样来跑,于是顺带介绍一下如何打war包。
修改packaging为war包:
导入依赖:
org.springframework.boot
spring-boot-starter-tomcat
provided
org.apache.tomcat.embed
tomcat-embed-jasper
provided
继承SpringBootServletInitializer
@SpringBootApplication
public class SecondkillApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(SecondkillApplication.class, args);
}
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(SecondkillApplication.class);
}
}
使用idea 工具生成war包,可以先clean,然后直接package。
这就打包成功了。
先放到本地tomcat下,然后启动tomcat试试:
tomcat启动后,会自动解压war包。打开浏览器,这样访问时,需要加上包名才能访问:
为了方便,直接把工程放到ROOT目录下:可以把刚才解压完的文件中的内容都拷贝到ROOT目录下:
完成!当然,如果你想打包的名字是工程名,就在pom文件中添加:
打包的名字就是工程artifactId了。