Java秒杀系统及优化---(4)

四、JMeter压测(主要是用来学习JMeter的使用,测试数据不具有参考意义)

  • JMeter入门
  • 自定义变量模拟多用户
  • JMeter命令行使用
  • SpringBoot打war包

1、JMeter入门

  • 官网:http://jmeter.apache.org/
  • 下载: http://jmeter.apache.org/download_jmeter.cgi
  • 用户手册: http://jmeter.apache.org/usermanual/index.html

JMeter压测验证:

win下压测

1.1)先下载jmeter

1.2)解压之后,找到bin目录下的jmeter.bat,运行(jmeter就是用java写的图形界面的应用程序)。

Java秒杀系统及优化---(4)_第1张图片

1.3)jmeter非常人性化,选项—选择语言,可以选择中文简体。

Options->Choose Language->Chinese(Simplified)

Java秒杀系统及优化---(4)_第2张图片

1.4)现在,我们先找个页面压测一下,压测前,我们先把程序都启动:

我们先来压测商品列表:/goods/to_list

右击测试计划,依次点击:添加---线程(用户)---线程组

  • 线程数:就是我们的并发数,我们先设置为10
  • Ramp-Up时间(秒):表示我们这10个线程是用了多长时间启动起来,如果是10,表示10秒钟把这10个线程启动起来,假如是1,表示1秒将10个线程启动,如果是0,那么就是10个线程一块启动起来。我们设置为0。
  • 循环次数:表示我们这10个线程,访问列表页面的时候循环多少次,如果是1,就是1次。

在线程组上右击---添加---配置元件---HTTP请求默认值,配置了这个之后,其他的元件中就不用重复在配置了。

Java秒杀系统及优化---(4)_第3张图片

在线程组上右击---添加---取样器---HTTP请求

Java秒杀系统及优化---(4)_第4张图片

在线程组上右击---添加---监听器---聚合报告,这个显示的比较粗略,详细的可以添加图形结果等元件。

点击运行按钮,点击YES,保存执行计划,暂时保存到桌面。

Java秒杀系统及优化---(4)_第5张图片

Java秒杀系统及优化---(4)_第6张图片

这就是结果,主要看这个Error和Throughput就行。

现在将线程数设置为1000,再来查看结果:

Java秒杀系统及优化---(4)_第7张图片

比原来大了好多,并发量在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命令来查看一下:

Java秒杀系统及优化---(4)_第8张图片

这是还没有进行测试时的情况,下面我们把并发量设置为1000,循环10次,进行压测:

Java秒杀系统及优化---(4)_第9张图片

Java秒杀系统及优化---(4)_第10张图片

这里可以很明显的看到,当压测开始时,我们的mysql进程直接排到前面来,这就说明了瓶颈是在数据库。

我是在自己电脑上装的虚拟机,只是简单做个测试,演示一下压测的效果。

2、自定义变量模拟多用户

这次我们压测另一个接口:因为我们说,我们系统很多时候都要获取用户这个对象,我们来看一下,假如说我们什么也不做,单纯的来获取这个对象看一看我们系统的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的值从哪里获取?

Java秒杀系统及优化---(4)_第11张图片

Java秒杀系统及优化---(4)_第12张图片

我们就用这个token就行。其他的跟原来一样,并发量1000,循环10次:

Java秒杀系统及优化---(4)_第13张图片

这个的吞吐量超过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这个方法占用时间多一些。

Java秒杀系统及优化---(4)_第14张图片

Java秒杀系统及优化---(4)_第15张图片

这里就是从redis中读取了一个值,再写入一个cookie,因为是对redis的操作,使用的是缓存,所以吞吐量更高点。而之前的商品列表,涉及到数据库的操作,所以吞吐量比较低些。

当然,这样测试也不太合适,毕竟只用一个用户,进行多次测试。现在尝试多用户的测试。

右击线程组---添加---配置元件---CSV数据文件设置

Java秒杀系统及优化---(4)_第16张图片

新建一个文件,随便起个名config,内容是:userId,userToken

引用文件中的变量:

Java秒杀系统及优化---(4)_第17张图片

再测试一下(其实这里是看不出来什么的......,要不就在程序中添加输出语句,看看效果......)

Java秒杀系统及优化---(4)_第18张图片

3、JMeter命令行使用(linux下的JMeter)

因为有时候我们就像直接在我们的服务器上做压测,因为我们的服务器大多数情况下都是linux的,没有图形界面的,我们来看一下,在命令行下面我们是如何使用的。

  • 3.1)在windows上录好jmx
  • 3.2)命令行:sh jmeter.sh -n -t XXX.jmx -l result.jtl
  • -n表示不使用图形界面,-t表示测试的脚本,-l表示输出的结果到后面的文件。
  • 3.3)把result.jtl导入到jmeter

这里做测试时,为了方便,还是用jar包。后面会简单说一下打war包。

先把jdk、redis都安装配置好,然后上传jmeter、jar包和需要的配置文件

Java秒杀系统及优化---(4)_第19张图片

我先上传了jmeter和jar包,使用nohup和java来运行jar包,nohup的作用就是:忽略输入,并把输出追加到"nohup.out"。

我们看一下这个文件的内容:实际上就是IDEA控制台输出的内容

Java秒杀系统及优化---(4)_第20张图片

打开浏览器,这里使用的是虚拟机的地址来访问:

Java秒杀系统及优化---(4)_第21张图片

程序正常运行。

我们还是先来压测商品列表,这次设置并发量5000,循环10次

Java秒杀系统及优化---(4)_第22张图片

将这个文件另存为goods_list,上传到服务器

Java秒杀系统及优化---(4)_第23张图片

从图中可以看到负载11.51,最高达到21+,而处理器只有两个,严重超载,当然,这并不是恒定不变的,有一段时间是两个java线程使用了更多的cpu,一个是secondkill,一个是jmeter本身。这个测试只是为了练习,不要太在意结果...

我们把测试结果下载下来,导入到jmeter中,看看内容:

Java秒杀系统及优化---(4)_第24张图片

Java秒杀系统及优化---(4)_第25张图片

在并发量5000,循环10次的情况下,Error为0,QPS:495.1,那我们再跑一次看看:

Java秒杀系统及优化---(4)_第26张图片

Java秒杀系统及优化---(4)_第27张图片

Java秒杀系统及优化---(4)_第28张图片

这次更高些(第一次算是预热)966。我们就把这次的数据作为一个参考,优化完之后再次进行压测,作比较。

下面我们来压测/miaosha/do_miaosha,这个算是我们的核心代码了,这里先改一下数据库时间,使其可以秒杀,库存都设置为10

因为“秒杀”这个方法需要user和goodsId,这个user我们使用token来模拟。我们先来生成5000个用户和token,这个生成的程序就不贴代码了,就是做循环......

要说的是:在生成数据时,调用doLogin有个小问题

Java秒杀系统及优化---(4)_第29张图片

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:

Java秒杀系统及优化---(4)_第30张图片

Java秒杀系统及优化---(4)_第31张图片

数据库也插入了5000条数据(还包括之前用来测试的那条数据):

Java秒杀系统及优化---(4)_第32张图片

再来设置JMeter脚本:

Java秒杀系统及优化---(4)_第33张图片

Java秒杀系统及优化---(4)_第34张图片

Java秒杀系统及优化---(4)_第35张图片

上传文件到服务器:

修改脚本文件中,引用tokens.txt的路径:

再进行压测,下载结果,导入JMeter查看:

Java秒杀系统及优化---(4)_第36张图片

Java秒杀系统及优化---(4)_第37张图片

Java秒杀系统及优化---(4)_第38张图片

这次负载达到40多了...

Java秒杀系统及优化---(4)_第39张图片

这个秒杀的压测,只有176(Throughput),确实挺低的,不过也是意料之中,因为秒杀这个接口做的事情比较多:

先是判断库存,然后是判断是否重复秒杀,最后是减库存下订单,都是与数据库进行交互,吞吐量低也正常。

我们跟之前一样,再重新压测一次:

Java秒杀系统及优化---(4)_第40张图片

并发量5000,循环10次,吞吐量555.

好了,测试先到此为止。

Java秒杀系统及优化---(4)_第41张图片

这是虚拟机的配置,其他的还有centos7.4

 

4、SpringBoot打war包

到目前为止,我们的程序都是打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。

Java秒杀系统及优化---(4)_第42张图片

Java秒杀系统及优化---(4)_第43张图片

这就打包成功了。

Java秒杀系统及优化---(4)_第44张图片

先放到本地tomcat下,然后启动tomcat试试:

Java秒杀系统及优化---(4)_第45张图片

tomcat启动后,会自动解压war包。打开浏览器,这样访问时,需要加上包名才能访问:

Java秒杀系统及优化---(4)_第46张图片

为了方便,直接把工程放到ROOT目录下:可以把刚才解压完的文件中的内容都拷贝到ROOT目录下:

Java秒杀系统及优化---(4)_第47张图片

Java秒杀系统及优化---(4)_第48张图片

完成!当然,如果你想打包的名字是工程名,就在pom文件中添加:

打包的名字就是工程artifactId了。

Java秒杀系统及优化---(4)_第49张图片

你可能感兴趣的:(Web框架)