性能测试的意义:
现状
互联网行业发展快,用户量大大增加;业务和系统架构越来越复杂,数据越来越多,用户不仅仅满足于功能的实现,在某些场景下,更在意系统性能。
什么是性能测试:
通过一定的手段,在多并发下情况下,获取被测系统的各项性能指标,验证被测系统在高并发下的处理能力、响应能力,稳定性等,能否满足预期。定位性能瓶颈,排查性能隐患,保障系统的质量,提升用户体验
什么样的系统需要做性能测试
◼ 用户量大,PV比较高的系统
◼ 系统核心模块/接口
◼ 业务逻辑/算法比较复杂
◼ 促销/活动推广计划
◼ 新系统,新项目
◼ 线上性能问题验证和调优
◼ 新技术选型
◼ 性能容量评估和规划
◼ 日常系统性能回归
事务
在性能测试领域里,衡量一个系统性能的好坏,主要看的是单位时间内,系统可以处理多少业务量。各个系统的业务各不相同,为了方便使用统一指标来衡量业务的性能。用事务来代表业务操作,一个事务可以代表一个业务,也可以代表多个业务操作。事务是用户定义的,想测试什么业务的性能,就把该业务加到事务中。
单个接口和多个接口都定义为一个事务。
TPS/QPS(Transaction Per Second): 每秒处理的事务数
响应时间=网络传输的总时间+各组件业务处理时间
平均响应时间:测试过程中所有请求的平均耗时
将所有请求的响应时间先从大到小进行排序,计算指定比例的请求都是小于某个时间。该指标统计的是大多数请求的耗时。
Tp90(90%响应时间):90%的请求耗时都低于某个时间
Tp95(95%响应时间):95%的请求耗时都低于某个时间
Tp99(99%响应时间):99%的请求耗时都低于某个时间
并发数/虚拟用户(Vuser):压测工具中设置的并发线程/进程数量
成功率:请求的成功率
PV(Page View):页面/接口的访问量
UV(Unique Visitor):页面/接口的每日唯一访客
吞吐量:网络中上行和下行的流量总和,吞吐量代表网络的流量,TPS越高,吞吐量越大
吞吐量单位一般为兆
TPS、响应时间、并发数的关系:
TPS=并发数/响应时间(响应时间单位为秒)
操作系统级别监控:
CPU使用率、内存使用率、网络IO(input/output)、磁盘(read/write/util)
中间件监控:
连接数、长短连接、使用内存
应用层监控:
线程状态、JVM参数、GC频率、锁
DB层监控:
连接数、锁、缓存、内存、SQL效率
需求调研
测试计划
环境搭建
数据准备
脚本编写
压测执行
调优回归
测试报告
组件使用参jmeter工具
Jmeter线程组两种运行模式:
◼ 按照运行次数运行:线程组设置循环x次
◼ 按照运行时间运行
1> 线程组设置循环永远
2> 勾选调度器,设置持续时间,单位
线程组和请求设置逻辑:
关键逻辑
◼ 一个线程组内的多个请求是顺序执行的
◼ 不同线程组内的请求是并行执行的
实际工作中如何设置线程组和请求的关系?
◼ 如果多个接口之间没有强依赖关系,每个接口单独设置一个线程组
◼ 如果多个接口之间有依赖关系(数据关联),把有关联关系的接口按照顺放在同一个线程组内
压力机:产生压力的机器,即jmeter所在的系统,可以是Windows系统或Linux系统。
服务器:部署项目的系统
压力机和服务器不能是一台机器
准备工作
1,压力机安装并配置好JDK、jmeter:
(1)上传jdk-8u221-linux-x64.tar.gz和 apache-jmeter-5.1.1.zip到Linux的/usr/local目录下
(2)解压:tar xvf jdk-8u221-linux-x64.tar.gz unzip apache-jmeter-5.1.1.zip
(3)修改配置文件:vi /etc/profile
(4)光标移动到最后一行,添加以下配置
JAVA_HOME=/usr/local/tools/jdk1.8.0_241
JMETER_HOME=/usr/local/tools/apache-jmeter-5.1.1
CLASSPATH=$:CLASSPATH:$JAVA_HOME/lib/
PATH=$PATH:$JAVA_HOME/bin:$JMETER_HOME/bin
export PATH JAVA_HOME CLASSPATH
(5)退出vi,执行命令source /etc/profile,让配置生效
(6)进到Jmeter 根目录下需要添加执行权限 chmod +x ./*
(7)执行java-version、jmeter -v命令,如果能看到版本信息,配置成功
3、将项目pinter.jar上传至Linux服务器,启动项目
4,在windows下调试好jmeter脚本,并上传至Linux下
Jmeter参数优化
文件路径:/usr/local/tools/apache-jmeter-5.1.1/bin/jmeter.properties
1,控制台取样间隔的设置
summariser.interval=10,默认为30s,最低可修改为6s
2,Jvm参数优化
bin目录下,vi jmeter,修改HEAP的size大小,默认1024M,可以设置成2048M(前提是内存够)
HEAP="-Xms2g -Xmx2g -XX:MaxMetaspaceSize=256m“
3,默认编码修改
sampleresult.default.encoding=UTF-8
单机器测试步骤
jmeter -n -t pinter.jmx -l result.jtl
-n: 命令行模式启动jmeter,no-gui
-t:jmx脚本路径;
-l:jtl结果文件存放路径
概要日志:
分布式测试步骤
分布式压测:多台压力机同时对服务器施加压力。
分布式测试步骤:
系统、etc/hosts文件中进行类似 本机IP 主机名的配置,如10.0.0.23 zhoucentos,否则启动jmeter server会报错!
1,在每台机器上都部署 jmeter
2,如果是java脚本,将java脚本和相关lib包都放在jmeter目录lib/ext下
3,将jmeter的场景文件jmx上传到主jmeter的任意位置,参数文件放到每一台压力机上(存放目录要相同)
4,修改jmeter.properties文件,ssl.disable=true(去掉注释)
5,在每台机器上进入到jmeter的bin目录下,都启动nohup ./jmeter-server &(后台启动jmeter-server服务)
6,在主jmeter的bin目录下,修改jmeter.properties,将其中的remote_hosts修改为作为压力机的两台机器ip,remote_hosts=127.0.0.1,192.168.0.102
7、关闭压力机的防火墙
8,在主jmeter的机器上,执行jmeter -n -t pinter.jmx -l result.jtl -r(示例,具体目录和路径自定义)
注意:
1、如果是http脚本,在controller的机器上有脚本文件即可;
2、如果是Java脚本,在每一台机器上都得有脚本文件和依赖的jar包
结果报表
三种方式来获取Jmeter的结果报表
一、在GUI模式下跑Jmeter的脚本,用tps插件实时展示图表(不建议使用)
二、在命令行模式下跑Jmeter的脚本,生成的jtl文件,在GUI界面的聚合报告里打开,可以展示tps和响应时间等数据
如果多个接口一起压测,jtl文件显示的是综合数据,聚合报告可以显示单个接口的数据
三、在命令行模式下跑Jmeter的脚本,生成的jtl文件,通过Jmeter自带命令,生成html报表
html报表
Html报表生成步骤:
1、进入jmeter的bin目录下,修改reportgenerator.properties
2、修改jmeter.reportgenerator.overall_granularity=1000(报表中数据展示间隔1秒)(根据压测时间自行修改)
3、创建一个存放数据报表的文件夹 mkdir report
4、执行命令:jmeter -g result.jtl -o ./report
其中:
-g 指定jtl文件的路径
-o 指定html报表生成到哪个文件夹下,report文件夹必须为空
5、压缩文件report:zip -r ./report.zip ./report
6、下载,解压,打开
注意:只有Jmeter3.0版本以上支持此功能
Linux下排查错误方法
1、jmeter.log(单机压测)/jmeter-server.log(分布式压测)查看是否有error
2、jtl文件查看failureMessage
3、windows界面查看:下载error.xml——》新建“查看结果树”——》浏览选择error.xml文件
cat命令适合查看短文件
more命令适合查看长文件
*** 性能测试目的***
测试系统最大处理能力
寻找系统最大的TPS,判断TPS和对应响应时间是否满足预期
测试系统支持最高并发
寻找系统最高能支持多少并发,当系统出现宕机、进程崩溃、报错率持续上升、响应时间超过可忍受范围、程序无响应等情况,即可认为系统达到了可支持的最高并发
性能测试场景
三个基本压测场景
1、先进行单接口测试
2、再按照一定的并发比例,进行多接口混合测试
3、最后按照混合场景比例,进行长时间稳定性测试(12h),稳定性测试关注tps和响应时间是否稳定波动是否大
其他压测场景
根据自己的业务情况,选择不同业务场景的压测
性能测试执行策略
加压策略
从小并发开始,逐步增加并发,寻找性能拐点
执行策略
试压阶段:寻找拐点,记录拐点数据
收集数据:选择拐点前后5组数据,按照固定时间(3-5分钟)重新跑一次,记录详细数据
性能拐点
并发数 | TPS | 平均响应时间(ms) |
---|---|---|
10 | 21 | 54 |
20 | 45 | 67 |
30 | 95 | 123 |
40 | 150 | 167 |
50 | 290 | 205 |
60 | 241 | 287 |
70 | 196 | 324 |
80 | 151 | 386 |
再谈TPS、响应时间、并发
注册用户 > 在线用户 > 并发用户
并发用户又分为真实并发用户和服务端并发用户
真实并发用户 > 服务端并发
总结:
在压测工具中设置的并发数,其实就是服务端的并发数
不需要关注真实用户并发,关注服务端并发即可
一个系统的性能好坏,不能用支持的并发数还衡量,而是以TPS、响应时间来衡量
性能测试没必要过分关注并发数,而是更应该关注业务性能指标TPS、响应时间
性能差的系统,比性能好的系统支持更高的并发数
如果想让一个系统支持更高的并发数,只需要将系统的响应时间变长即可
软件 | 地址 |
---|---|
Mysql | VMwareCentOs7-002:192.168.74.133; 安装包: /usr/local/tools |
Tomcat/项目 | VMwareCentOs7-003:192.168.74.134; tomcat:/home/services; 项目:/home/app |
Nginx | VMwareCentOs7:192.168.74.132; nginx: /etc/nginx |
Redis | VMwareCentOs7-002:192.168.74.133; 安装包/源码: /usr/local/tools/redis-5.0.8; 安装位置:/usr/local/bin |
MySQL 安装配置:
1、 将群文件中的mysql-community-5.7- Linux -rpm.zip文件上传到Linux下的/usr/local/src
目录下(其他目录也可以)
2、 对压缩包进行解压 unzip mysql-community-5.7-rpm.zip,里面包含 4 个安装包
3、 分别执行以下命令安装四个包(严格按照顺序执行)
rpm -ivh mysql-community-common-5.7.28-1.el7.x86_64.rpm --force --nodeps
rpm -ivh mysql-community-libs-5.7.28-1.el7.x86_64.rpm --force --nodeps
rpm -ivh mysql-community-client-5.7.28-1.el7.x86_64.rpm --force --nodeps
rpm -ivh mysql-community-server-5.7.28-1.el7.x86_64.rpm --force --nodeps
4、 启动 MySQL
systemctl start mysqld
启动成功后,执行 ps -ef|grep mysql 命令查看进程是否存在
5、 查找生成的临时密码,并记录下来,如下图
grep ‘temporary password’ /var/log/mysqld.log
6、 登录 MySQL
mysql -u root -p
粘贴刚才记录的临时密码
7、 修改密码(注意密码长度大于 8 位,包含大小写字母+特殊符号+数字),以分号结尾
ALTER USER ‘root’@‘localhost’ IDENTIFIED BY ‘388712sunSXF#’;
8、 设置权限,允许用户远程访问
GRANT ALL PRIVILEGES ON *.* TO ‘root’@’%’ IDENTIFIED BY ‘388712sunSXF#’ WITH GRANT OPTION;
9、 退出 MySQL
exit
10、关闭防火墙
systemctl stop firewalld.service
提供了作为 Web 服务器的一些特有功能,如 Tomcat 管理和控制平台、安全域管理和 Tomcat 附加组件等。
web项目中间件,负责业务之外的底层逻辑处理,处理用户的并发访问,链接数,tcp请求转换。url映射
1、 先将项目放到 Linux (VMwareCentOs7)上tomcat的webapps目录下,/home/app/services/apache-tomcat-8.5.38-pinter/webapps
注意:如果项目是 war 包形式的,需要在webapps目录下先创建 pinter 文件夹,然后在文件夹内对 war 包进行解压:unzip pinter.war
单项目部署(pinter 项目)
1、 先将项目放到 Linux (VMwareCentOs7-003)上某个目录下,如/home/app/pinter
注意:如果项目是 war 包形式的,需要先创建 pinter 文件夹,然后在文件夹内对 war 包
进行解压:unzip pinter.war
2、 修改 tomcat 的 conf/server.xml 文件,在
注意:docBase部分改为自己项目的目录
3、 进到 bin 目录下,执行命令:
./startup.sh & tail -f …/logs/catalina.out
注意:第一次启动 tomcat 的时候,没有 catalina.out 文件,只需要执行./startup.sh
4、 查看控制台日志,项目是否启动成功
5、 关闭防火墙,浏览器访问 pinter 项目进行验证
http://{ip}:{port}/index
多项目部署(增加 TestOA 项目)
1、 将 OA 项目放到 Linux 目录下(和 pinter 同级目录)
2、 配置 OA 项目所需要的数据库信息(数据库必须启动,且已经导入了 oa.sql)
TestOA/WEB-INF/classes/jdbc.properties 文件,修改数据库的连接信息
3、 复制一份 tomcat,重命名为 tomcat-oa
cp -r apache-tomcat-8.5.38-pinter/ apache-tomcat-8.5.38-oa
4、 修改 tomcat-oa 的 conf/server.xml 文件
a>
b>
5、 启动 tomcat,查看控制台日志,项目是否启动成功
6、 浏览器访问 OA 项目进行验证
http://{ip}:{port}/homeAction_index.action
Tomcat 访问日志配置
tomcat 的 logs 目录下 localhost_access_log.xxxx-xx-xx.txt 里记录了访问 tomcat 的日志,可
以通过配置,使访问日志中显示请求耗时
修改 tomcat 的 conf 目录下 server.xml 文件,在 pattern 属性里添加%T
pattern="%h %l %u %t “%r” %s %b %T" />
重启 tomcat,查看 access 日志,红框里代表就是请求的耗时,单位秒
Nginx:高性能反向代理服务器,官网数据显示每秒tps在50w左右
代理客户端是正向代理,代理服务端是反向代理
作用:提供了一种统一的访问地址;负载均衡
web服务器:转发请求,nginx,apache,类似于服务员/中介
应用服务器:tomcat,处理动态的语言,处理逻辑,类似于厨师/房东
官网:https://www.nginx.com/
Nginx 安装前需要先安装 pcre、openssl、zlib 等模块,手动安装比较繁琐,所以推荐使用
yum 进行安装
1、 默认情况下 CentOS7 中没有 nginx 的下载源,先手动添加官方源
rpm -Uvh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm
2、 安装 Nginx
yum install -y nginx
Nginx 默认安装到/etc/nginx 目录下,nginx.conf 为主配置文件
编辑 nginx.conf
修改 worker_processes 为当前系统的 CPU 核数
在 Nginx 中,每个项目都有自己的一个配置文件,配置了本项目的访问方式
1、进入 nginx 目录下的 conf.d 文件夹,包含了一个默认配置文件模板 default.conf
2、重命名配置文件
mv default.conf pinter.conf
3、 编辑 pinter.conf 配置文件,在 server 模块前面新增
upstream www.pinter.com {
server 192.168.74.134:8080 weight=10 max_fails=2 fail_timeout=30s;
server 192.168.74.134:8100 weight=10 max_fails=2 fail_timeout=30s;
}
注意:红色字体根据实际情况修改,两个 ip 为本项目部署在哪些服务器上
4、 将 server_name 修改为 www.pinter.com(根据实际情况修改),access.log 的注释去掉,
后面文件名称改为项目名称
5、将 location 中的 root、index 注释掉,新增 proxy_pass http://www.pinter.com;
注意:proxy_pass 后的域名名称必须和 upstream 名称保持一致
6、 conf.d 目录下可以放多个项目,配置方法同上
7、 启动 nginx
在任意目录下执行命令:nginx
如果没有任何报错,就代表启动成功了,nginx默认是80端口
nginx -s reload:重启 nginx
nginx -s stop:停止 nginx
8、 在本机(Windows/Mac),修改 host 文件,配置两个项目的 ip 和域名映射关系
Windows 系统 host 文件在 C:\Windows\System32\drivers\etc\hosts
Mac 系统在/etc/hosts
9、在浏览器中,通过域名的方式,分别访问两个项目,搞定
http://www.pinter.com/index
http://www.oa.com/homeAction_index.action
用户每次访问 nginx,都会在项目的 access.log 里记录一行日志。
可以在访问日志里记录请求耗时,这样方便排查问题
配置响应时间/请求耗时方法:在 Nginx 的主配置文件中:/etc/nginx/nginx.conf
重启 nginx,在项目 access.log 里,可以看到每个请求的耗时了
在 upstream 模块中,添加负载均衡策略
四种策略:
轮询:默认策略
ip_hash:根据 ip 进行 hash 算法,固定的 ip 分配到固定的后端 server
fair:根据后端 server 的响应时间来分配请求,响应时间短的优先分配
url_hash:根据 url 进行 hash 算法,固定的 url 分配到固定的后端 server
Redis是当前使用最广泛的NoSQL,而就Redis技术而言,它的性能十分优越,可以支持每秒十几万次的读/写操作,其性能远超数据库,并且还支持集群、分布式、主从同步等配置,原则上可以无限扩展,让更多的数据存储在内存中,更让人欣慰的是它还支持一定的事务能力,这保证了高并发的场景下数据的安全和一致性。
redis的数据存在内存中
Redis应用场景
1、缓存
2、消息队列,比如支付
3、活动排行榜或计数
4、发布,订阅消息(消息通知)
5、商品列表,评论列表等
6、有效期控制
Yum 官方源里没有 redis,所以采用手动源码安装
1、 上传 redis 安装包到 Linux 任意目录下,解压 tar xvf redis-5.0.7.tar.gz
2、 安装 gcc(redis 安装依赖 C 语言环境,需要先安装 gcc)
yum install -y gcc
3、 进入解压后的 redis 目录下,执行编译操作
make MALLOC=libc
4、 执行安装命令
make install
5、 安装成功
1、 在 redis 目录下,新建 conf 文件夹,将 redis.conf 配置文件拷贝到 conf 文件夹下,并重
命名为 6379.conf
mkdir conf
cp redis.conf ./conf/6379.conf
2、 修改 redis.conf 配置文件
daemonize 改为 yes
dbfilename 改为 dump_6379.rdb
将 bind 127.0.0.1 注释掉
protected-mode 改为 no
1、 先启动 server,在 conf 目录下,执行
redis-server ./6379.conf
2、 使用自带的客户端登录 redis,默认连接的是 6379 端口的 redis 实例
redis-cli
3、 关闭 redis-server
redis-cli shutdown
传统关系型数据库
id | name | age | address | phone |
---|---|---|---|---|
1 | sxf | 18 | 杭州市 | 15988861234 |
2 | xfsun | 20 | 上海市 | 15988865678 |
redis中string数据结构
key | value |
---|---|
id_1 | sxf ,18, 杭州市, 15988861234 |
id_2 | xfsun ,20 ,上海市,15988865678 |
对String数据类型的操作
set key value:给名称为key的String值赋值为value
get key:返回名称为key的value值
keys *:查看所有的key(公司中慎用,可能造成redis卡死)
对List数据类型的操作
rpush key value:在名称为key的List尾部添加一个值为value的元素
lpush key value:在名称为key的List头部添加一个值为value的元素
llen key:返回名称为key的List的长度
lrange key start end:返回名称为key的List中start至end之间的元素
lset key index value:给名称为key的list中index位置的元素赋值为value
rpop:返回并删除名称为key的list中的尾元素
对Hash数据类型的操作
hset key field value:向名称为key的hash中添加元素field<—>value
hget key field:返回名称为key的hash中field对应的value
hgetall:返回名称为key的hash中所有的键(field)及其对应的value
hlen key:返回名称为key的hash中元素个数
hdel key field:删除名称为key的hash中键为field的域
对全局value的操作
exists key:确认一个key是否存在
del key:删除一个key
type key:返回值的类型
keys pattern:返回满足给定pattern的所有key
dbsize:返回当前数据库中key的数目
select dbindex:切换数据库
flushdb:删除当前选择数据库中的所有key(危险操作)
flushall:删除所有库中的所有key(危险操作)
持久化:将内存中的数据保存到磁盘中
Redis的两种持久化机制
rdb:在指定的时间间隔内将内存中的数据集快照写入磁盘
优点:性能最大化 、如果数据集很大,RDB的启动效率会更高
缺点:数据安全性差
aof:以日志的形式记录服务器所处理的每一个写、删除操作,查询操作不会记录
优点:数据安全性高
缺点:对于相同数量的数据集而言,AOF文件通常要大于RDB文件。恢复数据慢
多实例:一个服务器启动多个redis
由于redis服务端是单线程实现的,因此只能占用CPU的单核,为了充分利用CPU资源,可以在一台服务器上同时启动多个redis-server实例
1、 在 conf 目录下,拷贝一份配置文件,如
cp 6379.conf 6380.conf
2、 修改 6380.conf 文件
port 修改为 6380
dbfilename 修改为 dump_6380.rdb
3、 启动 redis-server
redis-server ./6380.conf
4、 使用客户端登录 redis
redis-cli -p 6380
读写分离 备份数据
在从 redis 中,新增 slaveof 127.0.0.1 6379,指向主 redis 的 ip 和端口
maxclient:最大连接数,默认是 10000
maxmemory:最大内存
requirepass:设置密码(redis-cli -a 密码),主redis配置密码后,从redis需要在配置文件中修改masterauth项添加密码才能同步数据
性能监控是性能测试过程中非常重要的一个环节,当在压测过程中出现性能瓶颈时,需要综合详细的监控数据对问题进行分析。整个系统架构中的每一个环节都需要做监控(压力机、网络、各中间件、各服务器硬件资源等)。性能监控做好了,就能帮助你快速的定位问题,找到系统的性能瓶颈。
free命令
free -m 以MB为单位显示系统内存的使用情况,同理,也可以使用-k、-g等其他的单位显示
buffer和cache:两者都是Linux下的缓存机制,其中buffer为写操作的缓存,cache为读操作的缓存
swap:交换空间,磁盘上的一块空间,当系统内存不足时,会使用交换空间
iostat命令
安装:yum install sysstat
iostat命令可以查看当前机器磁盘io的数据
iostat -x -k 1
-x:展示磁盘的扩展信息
-k:以k为单位展示磁盘数据
1:每1秒刷新一次
展示结果
util:磁盘IO使用率,单位%,反映磁盘的繁忙程度,上限100%
r/s:每秒读请求数
w/s:每秒写请求数
rkb:每秒写磁盘字节数
wkb:每秒读磁盘字节数
df命令
df命令可以查看当前系统磁盘空间的使用情况
命令:df -h
du -sh * 查看当前目录下占用磁盘的情况
磁盘速度测试
命令:dd if=/dev/zero of=/export/ddtest bs=8k count=1000000 oflag=direct
vmstat命令综合了CPU、进程、内存、磁盘IO等信息
vmstat 1
r 表示运行队列(就是说多少个进程真的分配到 CPU),我测试的服务器目前 CPU 比较空闲,
没什么程序在跑,当这个值超过了 CPU 数目,就会出现 CPU 瓶颈 了。这个也和 top 的负载
有关系,一般负载超过了 3 就比较高,超过了 5 就高,超过了 10 就不正常了,服务器的状
态很危险。top 的负载类似每秒的运行队 列。如果运行队列过大,表示你的 CPU 很繁忙,
一般会造成 CPU 使用率很高。
b 表示阻塞的进程,这个不多说,进程阻塞,大家懂的。
dstat是一个全能监控工具,整合了CPU、内存、磁盘、网络等几乎所有的监控项,支持实时刷新
dstat需要先进行安装:yum install -y dstat
监控命令:dstat -tcmnd --disk-util
nmon是IBM公司开发的Linux性能监控工具,可以实时展示系统性能情况,也可以将监控数据写入文
件中,并使用nmon分析器做数据展示
官网下载地址:http://nmon.sourceforge.net/pmwiki.php?n=Site.Download
./nmon_x86_64_centos7即可查看监控数据
报表模式:
命令:./nmon -ft -s 5 -c 1000
Nmon文件需要关注的标签页
1、cpu_all
2、diskbusy
3、net
4、mem
添加用户命令:useradd xfsun
设置密码命令:passwd xfsun
388712sun
普罗米修斯监控平台可以对集群多台机器的系统资源进行监控
普罗米修斯官网:https://prometheus.io/
grafana官网:https://grafana.com/
平台监控原理
部署前的准备:
1、 关闭所有 Linux 机器的防火墙:systemctl stop firewalld.service
2、 保证所有 Linux 机器的时间是准确的,执行 date 命令检查;如果不准确,建议使用
ntp 同步最新网络时间
安装:yum install -y ntp
同步时间:ntpdate pool.ntp.org
部署 Linux 操作系统监控组件
部署 prometheus
1、 下载包
https://github.com/prometheus/prometheus/releases/download/v2.15.2/promethe
us-2.15.2.linux-amd64.tar.gz
2、 将 prometheus 上传到一台单独的 Linux 机器上,执行解压操作
tar xvf prometheus-2.15.2.linux-amd64.tar.gz
3、 进入到解压后的文件夹中,修改配置文件,添加要监控的服务器信息 10.0.0.71
vi prometheus.yml
在 scrape_configs 配置项下添加 Linux 监控的 job,其中
IP 修改为上面部署 node_exporter 机器的 ip,端口号为 9100,注意缩进
- job_name: ‘node’
static_configs:
- targets: [‘10.0.0.71:9100’]
4、 保存配置文件,启动 prometheus
nohup ./prometheus &
检查 nohup.out 日志,如果有以下信息,启动成功
在浏览器中访问部署的 prometheus:http://10.0.0.45:9090
部署 Grafana
1、 下载安装包
https://dl.grafana.com/oss/release/grafana-6.5.3-1.x86_64.rpm
2、 将 grafana 安装包上传至 Linux 服务器(和 prometheus 使用同一台即可)
3、 安装 grafana
yum localinstall -y grafana-6.5.3-1.x86_64.rpm
4、 启动 grafana
systemctl start grafana-server
在浏览器访问:http://10.0.0.45:3000/
输入用户名/密码:admin/admin 登录
进行数据源配置,因为我的 prometheus 和 grafana 装在同一台机器上,所以 ip 写
的是 localhost,如果没在一台机器上,写上 prometheus 的 IP
点击“save and test”,如果提示 success,代表配置成功
6、 导入监控模板
打开 grafana 官网,查找官网提供的 prometheus 监控模板
https://grafana.com/grafana/dashboards
点击 Linux 服务器监控的中文模板,记录该模板的 id:8919
在 grafana 系统页面中,通过 id 导入该模板,即可在 grafana 中看到 10.0.0.71 机器
的性能监控数据
将数据更新频率设置为 5s,展示最近 5 分钟的数据,就可以看到实时的、最近 5 分
钟的各项性能指标。包含了 CPU、Load、内存、网络、磁盘、IO 耗时等指标。监控
数据永久保存,可以随时查看任意时间点内的历史统计数据,非常方便。
附录:添加对 MySQL 的监控
1、 下载 MySQL 的 exporter
https://github.com/prometheus/mysqld_exporter/releases/download/v0.12.1/mysqld_exporter-0.12.1.linux-amd64.tar.gz
2、 上传至 MySQL 服务器上,解压包
tar xvf mysqld_exporter-0.12.1.linux-amd64.tar.gz
3、 执行命令
export DATA_SOURCE_NAME=‘root:Testfan#123@(127.0.0.1:3306)/’
标红部分根据实际情况修改,其中:
root 为数据库用户名
Testfan#123 为数据库密码
127.0.0.1 为数据库 IP
3306 为数据库端口号
4、 启动 mysql exporter
进入到解压后的文件夹中,执行命令
nohup ./mysqld_exporter &
监控 nohup.out 日志,有如下日志代表成功
修改 prometheus 的配置文件 prometheus.yml
vi prometheus.yml
增加监控 MySQL 的 job(注意缩进格式)
- job_name: ‘mysql’
static_configs:
- targets: [‘10.0.0.71:9104’]
targets 中的 IP 为 mysql_exporter 的 IP,端口号固定为 9104
重启 prometheus,进入 prometheus 的 UI 界面,在 status-targets 页面下,可以看到 MySQL exporter 的状态
5、 在 Grafana 中添加 MySQL 的监控模板
在 grafana 官网模板中找到 MySQL 监控模板 Mysql Overview,id 为 7362,并导入
到 Grafana 中(具体操作步骤参考上面导入 Linux 监控模板操作)
这样在 grafana 中,就可以轻松监控 MySQL 的连接数、内存、表锁、慢查询、网
络、查询缓存等监控数据
线程的五种状态
无需安装,jdk自带,只能监控tomcat部署的项目
图形界面工具,只能在windows/mac系统使用,监控之前先对jvm加监控参数,在tomcat的bin目录下,catalina.sh文件中,第二行
添加:
JAVA_OPTS="-Dcom.sun.management.jmxremote.port=10086 -Dcom.sun.management.jmxremote.ssl=false -
Dcom.sun.management.jmxremote.authenticate=false -
Djava.rmi.server.hostname=10.0.0.9"
注意:
1-改port
2-改hostname为本机ip
启动项目后,windows控制台输入jvisualvm,系统会自动打开界面
jar包的项目也可以使用jvisualvm监控进程,启动命令:java -jar -Dcom.sun.management.jmxremote.port=10086 -Dcom.sun.management.jmxremote.ssl=false -
Dcom.sun.management.jmxremote.authenticate=false -
Djava.rmi.server.hostname=192.168.74.134 pinter.jar
jvisualvm添加远程主机即可(参上)
命令:jstack
nid=0x15cf:对应系统线程id(NativeThread ID),和top命令查看的线程pid对应,不过一个是
10进制,一个是16进制。
通过命令:top -H -p pid,可以查看该进程的所有线程信息
保存监控线程状态:jstack 2991 > dump.txt
监控jvm的GC情况
jstat -gcutil pid 2000 1000
监控2000次 1000ms刷新一次
S0:s0区当前使用比例
S1:s1区当前使用比例
E:伊甸园区使用比例
O:老年代使用比例
M:元空间区使用比例
CCS:压缩使用比例
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间
查看jvm配置信息
jmap -heap pid
可以看到java进程的堆的配置信息,各区的空间大小和配置信息
查看jvm中类和对象的占用情况
(一)jmap -histo 5279 | head -20:查看jvm中各个类的实例数、占用内存数量以及类的全名
(二)堆文件dump
jmap -dump:format=b,file=m.hdump 17777
对堆内存进行dump,以文件的形式进行保存下来,可以用jvisualvm等工具对文件进行分析
此命令与在Jvisualvm中点击堆 Dump功能一样,都会产生一个文件
Jvisualvm
比较重要的一些参数:
Clients
connected_clients:已连接客户端的数量
blocked_clients:正在等待阻塞命令(BLPOP、BRPOP、BRPOPLPUSH)的客户端的数量
Memory
used_memory_rss_human:以人类可读的格式,从操作系统的角度,返回 Redis 已分配的内
存总量(俗称常驻集大小)。这个值和 top 、 ps 等命令的输出一致。
Stats
keyspace_hits:命中次数
keyspace_misses:没命中次数
界面形式展示redis监控数据
下载redis-stat-0.4.14.jar
在redis服务器上执行:java -jar redis-stat-0.4.14.jar --server [–auth 密码]
关闭Linux防火墙,在浏览器输入
http://192.168.2.241:63790/
监控其他端口redis:java -jar redis-stat-0.4.14.jar 127.0.0.1:6380 --server [–auth 密码]
Nginx重点监控链接数,一般是通过netstat命令来监控
监控80端口的连接数情况
netstat -anp | grep :80
查看80端口总连接数
netstat -anp | grep :80 | wc -l
查看nginx所有连接数的状态,分类展示
netstat -anp | grep :80 | awk ‘{print $6}’ | sort | uniq -c | sort -rn
参普罗米修斯
Java内存管理结构
Java采用了自动管理内存的方式
Java程序是运行在Jvm之中的
Java的跨平台的基于Jvm的跨平台特性
内存的分配和对象的创建是在Jvm中
用户可以通过一系列参数来配置Jvm
jvm运行时区域
特点:
• 线程私有,每个线程都有自己的栈内存,不能使用其他栈内存的数据
• 生命周期和线程相同,线程结束时内存随即消失
主要存放内容:
➢ 基本数据类型(int,char,float,double…)
➢ 对象的引用,指向了对象在堆内存中起始地址
➢ 通过-Xss参数配置
堆内存 = 年轻代+老年代
年轻代 = Eden+Survivor
Survivor = From Space(s0)+To Space(s1)
特点:
• 堆内存是Jvm中空间最大的区域
• 所有线程共享堆
• 所有的数组以及内存对象的实例都在此区域分配
• 堆内存大小通过参数进行配置
-Xmx:最大堆内存
-Xms:最小堆内存
Object o = new Object()
其中,o存放在栈内存中,new Object()存放在堆内存中,变量o是Object对象的引用,o上存放
了Object对象占用内存的起始地址
特点:
• 永久代也叫(Method Area)
• 各线程共享,主方法区要存放类信息、常量、静态变量,如public static int a = 10
• 垃圾回收行为比较少见
Java8新变化:
Java8从Jvm中移除了PermGen,使用Metaspace(元空间)来代替永久代
Metaspace不存在Jvm中,而是存在本地内存中
配置元空间初始值和最大值参数:
-XX:MetaspaceSize=64m
-XX:MaxMetaspaceSize=64m
三个问题
• 哪些内存需要回收?
• 什么时候回收?
• 如何回收?
◼ 新生代引发的GC叫YoungGC
◼ 老年代引发的GC叫FullGC,FullGC会引起整个Jvm的用户线程暂停,待垃圾回收完毕后,才继续运行
对象存活状态判断
确定对象“存活”还是“死去”
1、引用计数算法(如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称这块内存代表一个引用)
2、根搜索算法(GC Roots)
标记-清除算法(Mark-Swap)
特点
– 分为“标记”和“清除”两个阶段
– 标记完成后,统一回收
缺点
– 效率,标记和清除过程效率都不高
– 空间,标记清除后会产生大量不连续的内存碎片
复制算法
特点:
– 内存分为相等的两块
– 当一块内存用完,将存活对象复制到另外一块中,原内存一次性清理掉
– 复制时按照顺序分配内存,无内存碎片问题
– 新生代使用此算法
缺点:
– 将内存分为两半,利用率低
特点:
– 先对存活对象进行标记
– 让所有存活对象向一边移动
– 清理掉存活对象边界外的所有内存
注:老年代使用此算法
分代收集算法
当代的商业虚拟机都采用“分代收集”
• 根据对象的存活周期的不同将内存划分成几块,一般Java堆分为新生代和老年代
• 新生代采用复制算法
• 老年代采用标记-压缩算法
永久代回收“性价比”比较低
主要回收:
–废弃的常量
– 无用的类
• 类的所有实例都已经被回收
• 加载该类的ClassLoader已经被回收
• 该类的Class对象没有在任何地方被引用
垃圾收集器是内存回收算法的具体实现
• 没有完美的收集器
• Jvm不同的区域可以采用不同的垃圾收集器组合,主要有:
– Serial收集器(串行)
– ParNew收集器(并行)
– CMS收集器(并发)
– G1(时间优先)
• 单线程收集器
• 用户线程全部停止(Stop the world)
• Client模式下,新生代默认收集器
• 优点:简单、高效
并行收集器,Serial收集器的多线程版本
• Server模式下Jvm默认的新生代收集器
• 默认开启的垃圾回收线程与cpu核数一致
并发收集器(ConcurrentMarkSweep)
• 采用了标记-清除、标记-压缩算法
• 并发收集、低停顿
• 缺点:
– 消耗cpu
– 会产生内存碎片
– 浮动垃圾(Concurrent Mode Failure)
G1全称是Garbage First Garbage Collector,在jdk1.7u4中开始支持。Java9中默认的垃圾收集器。
◼ G1的设计原则就是简化性能优化的复杂性
堆内存溢出(一般运行过程中会出现此问题)
– 堆内存中存在大量对象,这些对象都有被引用,当所有对象占用空间达到堆内存的最大值,
就会出现内存溢出OutOfMemory:Java heap space
永久代溢出(一般启动阶段会出现此问题)
– 类的一些信息,如类名、访问修饰符、字段描述、方法描述等,所占空间大于永久代最大
值,就会出现OutOfMemoryError:PermGen space
内存溢出的检测方法
Jdk/bin目录下有很多检测工具
• 图形界面:
– Jconsole
– Jvisualvm
• 命令行工具
– Jstat –gcutil pid 1000 100
– Jmap –histo pid | head -20
– Jmap –heap pid
FullGC频率:建议单次FullGC时间<200ms
Jvm常见参数一:
• -Xms2048m,初始堆大小,建议<物理内存的1/4,默认值为物理内存的1/64
• -Xmx2048m,最大堆大小,建议与-Xms保持一致,默认值为物理内存的1/4
• -Xmn512m,新生代大小,建议不超过堆内存的1/2
• -Xss256k,线程堆栈大小,建议256k
• -XX:PermSize=256m,永久代初始值,默认值为物理内存的1/64
• -XX:MaxPermSize=256m,永久代最大值,默认值为物理内存的1/4
• -XX:SurvivorRatio=8:年轻带中Eden区和Survivor区的比例,默认为8:1:1,即Eden(8),
From Space(1),ToSpace(1)
•-XX:MaxTenuringThreshold=15:晋升到老年代的对象年龄,每个对象坚持过一次MinorGC后对象
年龄+1,默认值是15,年龄超过15进入到老年代,该参数在串行GC时有效
•-XX:PretenureSizeThreshold=3145728:单位字节,只对Serial和ParNew两款收集器有效,新生代
采用Parallel Scavenge GC时无效,大于这个值的对象直接在老年代进行分配
• -XX:+UseConcMarkSweepGC:开启CMS垃圾回收器
JAVA_OPTS="-Xms256m -Xmx256m -XX:MetaspaceSize=64m -XX:MaxMetaspaceSize=64m -Xss1024k -XX:+UseConcMarkSweepGC"
Jvm常见参数二:
CMS相关参数:
-XX:+UseConcMarkSweepGC:默认关闭,ParNew+CMS+Serial Old,当CMS收集器出现
ConcurrentModeFailure错误(Jvm预留空间不足以容纳程序使用),采用后备收集器Serial Old
-XX:CMSInitiatingOccupancyFraction=80:CMS收集器在老年代空间被使用多少时触发FullGC,默认为92
-XX:+UseCMSCompactAtFullCollection:CMS收集器在FullGC时开启内存碎片的压缩,默认关闭
-XX:CMSFullGCsBeforeCompaction=8:执行多少次不压缩FullGC后,进行一次压缩,默认是0(代表每次FullGC都进行压缩)
-XX:+UseCMSInitiatingOccupancyOnly:使用手动定义初始化定义开始,禁止hostspot自行触发CMS GC
-XX:ParallelGCThreads=8:并行收集器的线程数,此值最好配置与处理器数目相等 同样适用于CMS
日志参数:
-XX:+HeapDumpOnOutOfMemoryError:当发生内存溢出时,进行堆内存dump-XX:+PrintGCDetails:打印GC的详细信息
某测试环境服务器jvm配置
-server -Xms1028m -Xmx1028m -XX:PermSize=256m -XX:MaxPermSize=256m -Xmn512m
-XX:MaxDirectMemorySize=1g -XX:SurvivorRatio=10 -XX:+UseConcMarkSweepGC
-XX:+UseCMSCompactAtFullCollection -XX:CMSMaxAbortablePrecleanTime=5000
-XX:+CMSClassUnloadingEnabled -XX:CMSInitiatingOccupancyFraction=80
-XX:+UseCMSInitiatingOccupancyOnly -XX:ParallelGCThreads=8
-Xloggc:/home/admin/logs/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps
-XX:+HeapDumpOnOutOfMemoryError-XX:HeapDumpPath=/home/admin/logs/java.hprof
内存泄露的现象
1,tps出现大幅波动,并慢慢降低,甚至降为0,响应时间随之波动,慢慢升高
2,通过jstat命令看到,Jvm中Old区不断增加,FullGC非常频繁,对应的FullGC消耗的时间也不
断增加
3,通过jconsole/jvisualvm可以看到,堆内存曲线不断上升,接近上限时,变成一条直线
4,日志报错java.lang.OutOfMemoryError: Java heap space
内存泄露怎么定位
1,通过jmap命令:jmap -histo pid | head -20,查看当前堆内存中实例数和占用内存最多的前20
个对象
2,通过jvisualvm,进行远程堆dump,然后把dump文件下载下来,用jvisualvm打开进行分析,可
以看到更直观的jvm中对象的信息
内存泄露的场景
在什么样的场景下监控内存泄露问题?
1,在试压阶段,或任意场景都可以考虑通过jvisualvm和jstat监控jvm的情况
2,在稳定性场景中,一定要关注Jvm内存使用的情况,在长时间的压测下,最容易看出内存泄露的问题
定义
线程死锁就是有两个线程,一个线程锁住了资源A,又想去锁定资源B,另外一个线程锁定了资源B,又想去锁定资源A,两个线程都想去得到对方的资源,而又不愿释放自己的资源从而造成一种互相等待,无法执行的情况
现象
出现死锁后,tps降为0,压力测试工具无法得到服务器的响应,服务器硬件资源空闲,通过jvisualvm去查看线程情况,至少两个线程一直处于红色的阻塞状态。死锁经常表现为程序的停顿,或者不再响应用户的请求。从操作系统上观察,对应进程的CPU占用率为零。
只要是是死锁必定有block,出现死锁后必须重启项目才能恢复正常
定位方法
通过jvisualvm或者jstack,进行线程dump,对线程状态进行分析,获取到哪行代码导致的死锁,如:
解决思路
避免嵌套加锁
减小锁粒度
定义
在多线程情况下,如果一个线程对拥有某个资源的锁,那么这个线程就可以运行资源相关的代码。而
其他线程就只能等待其执行完毕后,才能继续争夺资源锁,从而运行相关代码。
Log4j的日志级别
ALL 各级包括自定义级别
DEBUG 指定细粒度信息事件是最有用的应用程序调试
ERROR 错误事件可能仍然允许应用程序继续运行
FATAL 指定非常严重的错误事件,这可能导致应用程序中止
INFO 指定能够突出在粗粒度级别的应用程序运行情况的信息的消息
OFF 这是最高等级,为了关闭日志记录
TRACE 指定细粒度比DEBUG更低的信息事件
WARN 指定具有潜在危害的情况
级别越低,日志越多,日志量:ALL > DEBUG > INFO > WARN > ERROR > FATAL
线程阻塞问题排查流程
1、做线程dump
2、在dump文件中搜索关键字"BLOCKED"、“TIME_WAITTING”,查看每种状态的count数量
3、按照上述关键字搜索,查看跟本系统有关的业务代码堆栈信息
解决方案
1、减少代码中没有必要的日志输出
2、如果可以,提升日志级别,以减少日志
3、如果可以,更换其他的日志组件,如Log4j2、Logback等
现象
压测过程中,发现应用服务器的CPU使用率比较高(>90%)
两种情况
1、接口的性能非常好,比如响应时间<10ms,tps很高,此时CPU使用率高是正常的,不需要优化
2、接口性能不好,比如响应时间>200ms,tps很低,此时需要考虑优化
CPU消耗高可能的原因
1、使用了复杂的算法,比如加密、解密
2、压缩、解压、序列化等操作
3、代码bug,比如死循环