秒杀项目实践(二)——压测及问题分析

PunkLu 2019年12月22日 41次浏览

搭建云端虚拟机环境

Java环境安装

​ 参考

数据库环境安装

yum install mysql*
yum install mariadb-server
systemctl start mariadb.service
mysqladmin -u root password 123456  -- 初始化mysql root用户密码
mysql -uroot -p123456   -- 登录
-- 开启端口:
firewall-cmd --zone=public --add-port=3306/tcp --permanent
firewall-cmd --reload

​ 授予远程连接数据库的权限:

授权法

GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '123456' WITH GRANT OPTION; // %:表示从任何主机连接到mysql服务器,

FLUSH   PRIVILEGES;

或者

GRANT ALL PRIVILEGES ON *.* TO 'root'@'116.30.70.187' IDENTIFIED BY '123456' WITH GRANT OPTION;//表示从指定ip从任何主机连接到mysql服务器

FLUSH   PRIVILEGES;

将之前本地的seckill数据转储为SQL文件,执行到云环境的seckill数据库中。

项目打包上传

切换到项目所在目录,使用以下命令打包:

mvn clean package

将打出来的target目录下的seckill-1.0-SNAPSHOT.jar文件上传到服务器上,

使用以下命令启动项目:

// 开启防火墙
firewall-cmd --zone=public --add-port=8090/tcp --permanent
firewall-cmd --reload
// 启动项目
java -jar seckill-1.0-SNAPSHOT.jar

外挂配置文件

新建application.properties外载配置文件,在其中填写想要个性化的配置信息,然后通过以下方式指定刚才新建的配置文件启动项目:

java -jar seckill-1.0-SNAPSHOT.jar --spring.config.addition-location=application.properties

编写deploy脚本

编写deploy脚本:

vi deploy.sh
nohup java -Xms400m -Xmx400m -XX:NewSize=200m --XX:MaxNewSize=200m -jar seckill-1.0-SNAPSHOT.jar --spring.config.addition-location=application.properties &

​ 授予deploy脚本执行权限:

chmod -R 777 deploy.sh

JMeter性能压测

关注点

  1. 线程组

    线程组允许启动多个并发线程测试并发

  2. Http请求

    发送Http请求获取返回结果,分析异常返回原因定位代码问题

  3. 查看结果树

  4. 聚合报告

    压测报告,内部含有JPS、QPS等性能指标

压测步骤

创建线程组

打开JMeter后,在测试计划上右键 添加-> 线程(用户)-> 线程组,线程数设置为100、Ramp-Up时间(秒)设置为10、循环次数设置为10,代表在10s中内创建100个线程、每个线程循环调用接口10次。

创建Http请求

在刚才创建好的线程组上右键,添加->取样器->HTTP请求,协议为http,服务器ip、端口输入虚拟机的ip、端口,方法为GET、路径填入获取商品详情的/item/get?id=6。在高级项中,将实现选择为Java。

创建结果树

在创建好的线程组上右键,添加->监听器->察看结果树

创建聚合报告

在创建好的线程组上右键,添加->监听器-> 聚合报告

查看服务器线程

安装工具:

yum install psmisc

使用以下命令定位项目的进程号:

ps -ef | grep java

使用以下命令查看项目有多少个线程:

pstree -p 进程号 | wc -l

使用以下命令查看服务器性能:

top -H

其中,cpu 分为用户态占用率和系统态占用率,其次,还应关注load average指标,load average表示CPU执行使用的时间。用户态、系统态占有率高不代表load average就高。

极限压测发现问题

将JMeter设置为以下参数:

参数项
线程数5000
Ramp-Up时间10
循环次数100

然后在服务器端执行

pstree -p 进程号 | wc -l

可以看到,服务器上为Java开启的线程数总共只有218个(我本机上),远小于JMeter上定义的500个线程数,由此可以得出结论:server端的并发线程数上不去,导致QPS容量上不去,导致性能出现瓶颈。

解决方法

是因为在spring-boot-autoconfiguration-x.x.x-RELEASE.jar中META-INF目录下的spring-configuration-metadata.json配置文件的配置项未修改,默认配置有性能瓶颈。

默认内嵌Tomcat配置项:

  1. server.tomcat.accept-count

    等待队列长度,默认100

  2. server.tomcat.max-connections

    最大可被连接数,默认10000

  3. server.tomcat.max-threads

    最大工作线程数,默认200

  4. server.tomcat.min-spare-threads

    最小工作线程数,默认10

默认配置下,连接超过10000后出现拒绝连接情况。

默认配置下,触发的请求超过200+100后拒绝处理。

所以修改服务器上外挂的application.properties,增加以下内容:

# 设置等待队列长度为1000
server.tomcat.accept-count=1000
# 4核8g配置的服务器最佳设置最大工作线程数为800
server.tomcat.max-threads=800
# 设置最小工作线程数
server.tomcat.min-spare-threads=100

然后重启项目,再次使用以下命令查看线程数:

pstree -p 进程号 | wc -l

可以看到线程数为大于100的值,因为配置里设置了最小工作线程数为100。

再次进行压测,可以看到,不再有异常响应,而且JMeter中的各项指标明显都有了显著提高。

定制化内嵌Tomcat开发

  1. keepAliveTimeOut

    多少毫秒后不响应的断开keepalive

  2. maxKeepAliveRequests

    多少次请求后keepalive断开失效

因为spring-configuration-metadata.json配置文件中没有这两个选项的默认配置,所以需要使用WebServerFactoryCustomizer定制化内嵌tomcat配置。

优化方向

经过以上参数调整优化后,可以发现性能已经有了显著提升,但是响应时间仍旧较长、QPS依然不够高。

单Web容器上限

  1. 线程数量

    4核CPU 8G内存单进程调度线程数800-1000以上后即花费巨大的时间在CPU调度上

  2. 等待队列长度

    队列做缓冲池用,但也不能无限长,消耗内存,出队入队也耗CPU

MySQL数据库QPS容量问题

  1. 主键查询

    千万级别数据 = 1-10毫秒

  2. 唯一索引查询

    千万级别数据 = 10-100毫秒

  3. 非唯一索引数据

    千万级别数据 = 100-2000毫秒

  4. 无索引

    百万条数据=1000毫秒+

MySQL数据库TPS容量问题

非插入更新删除操作:同查询

插入操作:1w ~ 10w tps(依赖配置优化)