1、总结tomcat优化方法
外部环境JVM优化
heap内存分配
主要是对JVM组成中的Runtime Data Areas(运行时数据区)进行优化,在运行时数据区中,heap(堆)存放的是创建的所有对象信息,是优化的重点目标,调整或限制内存的使用可以防止影响其他程序。
- 内存分配可以配置最大值和初始值,建议将两个值设置为一样
- -Xms:设置年轻代+老年代的初始heap内存大小;-Xmx:设置最大堆内存;可以通过-Xmn同时设置两则的值;
- -XX:NewSize:设置初始新生代大小;-XX:MaxNewSize:设置最大新生代内存空间;
- -XX:NewRatio:以比例方式设置新生代和老年代;-XX:SurvivorRatio:以比例方式设置eden和survivor
垃圾回收器
heap是依靠GC垃圾回收器进行管理的,垃圾回收器的功能主要是保证正常释放不再使用的内存空间,同时还需要尽可能的确保内存空间的连续性。
通过将堆内存进行分代,分为年轻代、老年代、永久代,对不同代内的对象执行不同的垃圾回收策略。
- 年轻代可以分为eden区、幸存区,幸存区是两个大小相等、地位相同、可互换的区,一个from区,一个to区。
- 年轻代的垃圾回收策略采用的是标记-清除+复制方式,新对象在eden区被创建,当eden区满后标记存活对象,将存活对象复制到幸存区;此后每当eden区满,都会对eden区和幸存区的对象执行标记-清除,并将存活对象复制到另外一个幸存区(幸存区有两个,每次复制算法会使用其中一个);
- 老年代:用于存放长时间存活的对象
- 当一个对象在年轻代被执行了默认15次复制(CMS垃圾回收器默认6次,通过-XX:MaxTenuringThreshold=N来修改);或者当一个幸存区与eden区都满了时,存活的对象将被复制到老年代;
- 老年代如果满了,就会对所有heap区执行清理,所有区域都触发垃圾回收,老年代执行的是标记-压缩算法,将幸存对象标记,清理不带标记的对象后,将幸存对象向内存的一端移动,整理后存活对象连续的集中在内存的一端。
- 永久代:此空间不存在垃圾回收,关闭JVM会释放区域内存。此空间必须制定大小限制。
常用垃圾收集器:
- 新生代:serial、ParNew、Parallel Scavenge
- serial是独占、串行的,只有一个线程执行垃圾回收;
- -XX:+UseSerialGC:在新生代使用serial垃圾收集器,在老年代使用SerialOld垃圾回收器
- 独占,指的是垃圾回收器工作期间工作线程需要等待回收完毕后才能继续执行;独占和并发指的是垃圾回收进程和工作进程之间是否能同时工作。
- 串行,指的是单个垃圾回收器线程来完成回收工作;并行和串行是指的回收线程是多个并行还是单个串行。
- ParNew是并行、独占的,与serial除了多线程外没有区别,老年代经常和CMS配合使用,关注的重点是尽量缩短垃圾收集时用户线程的停顿时间,比较适合与用户交互的程序,能够提高响应速度,提升用户体验
- -XX:+UseParNewGC,手动指定使用ParNew收集器执行内存回收;
- -XX:ParallelGCThreads,限制线程数量,默认开启和CPU相同数量的线程数。
- Parallel Scavenge:并行、独占,可动态调节达到一个可控制的吞吐量
- -XX:+UseParallelGC,在新生代启用Parallel Scavenge垃圾收集器
- 通过两个参数来精确控制吞吐量。
- 一个是控制最大垃圾收集停顿时间的-XX:MaxGCPauseMillis;让收集器尽可能保证内存花费时间不超过这个参数设置的毫秒数,但并不是加快收集速度,而是会动态调整收集频率,因为系统会将新生代内存空间调小来加快速度。
- 以及直接设置吞吐量大小的-XX:GCTimeRatio,垃圾收集时间占总时间的比率。
- -XX:+UseAdaptiveSizePolicy,打开后不需要手动指定内存分配,JVM会根据系统运行情况收集新能监控信息,动态调整参数以提供最合适的停顿时间或者最大的吞吐量,这个就叫做GC自适应调节策略。这是ParNew和Parallel Scavenge的最大区别。
- Parallel Scavenge不能和老年代垃圾收集器CMS组合。
- serial是独占、串行的,只有一个线程执行垃圾回收;
- 老年代:Serial Old、Parallel Old、CMS(Concurrent Mark Sweep)
- Serial Old,是serial的老年代版本,串行、独占
- Parallel Old:老年代的Parallel Scavenge,并行、独占,也是关注吞吐量
- -XX:+UseParallelOldGC,老年代使用Parallel Old垃圾回收器
- -XX:ParallelGCThreads=N,在关注吞吐量的场景使用此选项增加并行线程数
- CMS:并发标记清除算法,特点是在某些阶段,比如并发标记时,尽量和工作线程一起运行来减少STW的时长(200ms以内,STW是垃圾收集时工作线程会停止工作,所以叫StopTheWorld),提升响应速度。
- CMS在初始标记(只标记GC Roots能直接关联到的对象,速度很快)、重新标记(修正并发标记期间,用户线程产生的垃圾)是串行的,会产生STW,其他并发标记、并发清理都是并行的,并且这些阶段的耗时更长,所以总体是并发的。
- -XX:+UseConcMarkSweepGC,开启老年代CMS垃圾回收器,启动后默认自动开启新生代ParNew
Tomcat自身优化
对JVM的内存空间优化选项
JAVA_OPTS="-server -Xms4g -Xmx4g -XX:NewSize= -XX:MaxNewSize= "
-server:服务器模式
-Xms:堆内存初始化大小
-Xmx:堆内存空间上限
-XX:NewSize=:新生代空间初始化大小
-XX:MaxNewSize=:新生代空间最大值
#生产优化案例
JAVA_OPTS="-server -Xms4g -Xmx4g -Xss512k -Xmn1g -XX:CMSInitiatingOccupancyFraction=65 -XX:+AggressiveOpts -XX:+UseBiasedLocking -XX:+DisableExplicitGC -XX:MaxTenuringThreshold=10 -XX:NewRatio=2 -XX:PermSize=128m -XX:MaxPermSize=512m -XX:CMSFullGCsBeforeCompaction=5 -XX:+ExplicitGCInvokesConcurrent -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods"
线程池的调整,调整Connector的参数
-
connectionTimeout
:连接超时时长,单位ms -
maxThreads
:最大线程数,默认200 -
minSpareThreads
:最小空闲线程数 -
maxSpareThreads
:最大空闲线程数 -
acceptCount
:当启动线程满了之后,等待队列的最大长度,默认100 -
URIEncoding
:URI 地址编码格式,建议使用 UTF-8 -
enableLookups
:是否启用客户端主机名的DNS反向解析,缺省禁用,建议禁用,就使用客户端IP就行 -
compression
:是否启用传输压缩机制,建议 "on",CPU和流量的平衡-
compressionMinSize
:启用压缩传输的数据流最小值,单位是字节 -
compressableMimeType
:定义启用压缩功能的MIME类型text/html, text/xml, text/css, text/javascript
-
2、java程序出现oom如何解决?什么场景下会出现oom?
当JVM因为没有足够内存来为对象分配空间,并且垃圾回收器也没有空间可回收时,系统会出现如下OOM日志:
Jul 10 10:20:30 kernel: Out of memory: Kill process 9527 (java) score 88 or sacrifice child
java的OOM日志:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:3332)
at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:674)
at java.lang.StringBuilder.append(StringBuilder.java:208)
at HeapOom2.main(HeapOom2.java:6)
通常出现OOM的原因是:
- 给应用分配的内存太少,一般是启动时的JVM参数指定太少
- 应用使用太多,并且用完没有释放,此时就会造成内存泄漏或内存溢出
- 可以通过JDK工具来分析原因,如jvisualvm配合Visual GC插件;或通过商业软件profiler定位出现问题的代码
解决思路:
- 限制java进程的max heap,并且降低java程序的worker数量,从而降低内存使用
- 给系统增加swap空间
- 设置内核参数,不允许内存申请过量,不推荐的做法;
- linux默认是允许内存申请过量的,系统寄希望于实际上用不到那么多内存,但如果出现内存过量时:
- linux通过OOM killer机制,挑选一个进程kill来腾出部分内存,还不够那就继续kill
- 也可以通过vm.panic_on_oom,当发生OOM时就自动重启系统
- 重启和kill都可能造成业务中断,linux 2.6之后,允许通过vm.overcommit_memory内核参数来禁止memory overcommit
- linux默认是允许内存申请过量的,系统寄希望于实际上用不到那么多内存,但如果出现内存过量时:
3、简述redis特点及其应用场景
redis特点:
- 开源、遵循BSD协议、基于内存的键值数据库
- 提供将内存通过网络远程共享的一种服务,类似的还有memcached
- redis可以支持10W qps,c语言实现
- redis是单线程的(基于数据库的操作是单线程),一个线程处理所有的网络请求,因为redis是纯内存操作,再加上带有I/O多路复用功能
- 支持数据的持久化,可以将数据保存在磁盘中
- 支持更多的数据类型:字符串、哈希、list、sets、sorted sets
- 支持数据备份,可以实现Master-slave模式的数据备份,另外也支持使用快照(内存数据的快照存储到rdb文件中)+AOF(类似mysql的binlog,将对数据的操作记录下来)
- 支持更大value的数据,相比memcached仅支持1MB数据,redis支持512MB(生产还是不建议超过2M)
- 支持集群横向扩展,memcached不支持集群功能,是通过客户端根据hash来写入不同主机实现集群,redis则可以配置多主集群,也能支持集群的横向扩展,集群内node本身也支持主备,因此可以同时提供高可用和性能扩展
redis的应用场景
- Session共享:常见Web集群中的Tomcat或PHP中的多Web服务器Session共享
- 缓存:提供数据查询功能,如商品信息、新闻内容等;可以配合mysql,从redis中读取数据,写入mysql同时同步给redis;
- 计数器:商品排行榜、商品浏览数和次数等相关数值统计
- 微博/微信等社交场合:多种数据类型可以提供共同好友、粉丝数、关注、点赞评论等
- 消息队列:ELK的日志缓存、部分业务的订阅发布系统等
- 地理位置:同样是基于多种数据类型,提供定位服务,实现摇一摇、附近的人、外卖等功能
4、对比redis的RDB、AOF模式的优缺点
- RDB对内存执行快照,保存了某个时间点内存中的所有数据,当出现问题时可以恢复到不同时间点的版本;但RDB不能实时保存数据,会丢失故障时到上一个时间点之间未保存的数据;
- RDB是通过父进程fork的子进程来执行的保存工作;但如果子进程在存在期间,发生了大量的写操作,会导致子进程执行复制操作,就会出现很多的page-fault,这样需要消耗不少性能在复制上
- AOF的数据安全性相对较高,根据使用的fsync策略(何时同步内存数据到磁盘),默认是1s同步一次,就算发生故障,也只会丢失1s内的数据;但根据fsync的策略不同,aof可能速度慢于rdb
- AOF会产生重复记录,比如对一个数据执行了创建和修改,则创建和修改两个操作都会被记录;
- AOF在恢复大数据集时需要比RDB久的时间,因为RDB是直接加载到内存,而AOF则需要一条条执行操作;
5、实现redis哨兵,模拟master故障场景
#!/bin/bash
#****************************************************************************************#
#Author: Yabao11
#QQ: what QQ,no QQ
#Date: 2022-01-04
#FileName: nginx.sh
#URL: https://github.com/yabao11
#Description: Test Script
#Copyright (C): 2022 All rights reserved
#*******************************定义颜色*************************************************#
RED="\e[1;31m"
GREEN="\e[1;32m"
SKYBLUE="\e[1;36m"
YELLOW="\e[1;43m"
BLUE="\e[1;44m"
END="\e[0m"
RandomColor="\e[1;32m"
#****************************************************************************************#
function Ostype {
if grep -i -q "release 6" /etc/centos-release;then
echo Centos6
elif grep -i -q Centos-8 /etc/os-release;then
echo Centos
elif grep -i -q Centos-7 /etc/os-release;then
echo Centos7
elif grep -i -q Ubuntu /etc/os-release;then
echo Ubuntu
elif grep -i -q "RedHat" /etc/os-release;then
echo Redhat
fi
}
function color {
RES_COL=60
MOVE_TO_COL="echo -en \E[${RES_COL}G"
SETCOLOR_SUCCESS="echo -en \E[1;32m"
SETCOLOR_FAILURE="echo -en \E[1;31m"
SETCOLOR_WARNING="echo -en \E[1;33m"
SETCOLOR_NORMAL="echo -en \E[0m"
echo -n "$1" && $MOVE_TO_COL
echo -n "["
if [[ $2 = "success" || $2 = "0" ]]; then
${SETCOLOR_SUCCESS}
echo -n " OK "
elif [[ $2 = "failure" || $2 = "1" ]]; then
${SETCOLOR_FAILURE}
echo -n "FAILED"
else
${SETCOLOR_WARNING}
echo -n "WARNING"
fi
${SETCOLOR_NORMAL}
echo -n "]"
echo
}
function redis_install {
yum -y install gcc jemalloc-devel systemd-devel wget || { color "编译软件安装失败!" 1;exit; }
[ -e /root/${redis_version}.tar.gz ] || wget http://download.redis.io/releases/${redis_version}.tar.gz -P /root/
tar xvf ${redis_version}.tar.gz || { color "文件解压失败" 1;exit; }
cd ${redis_version}
make USE_SYSTEMD=yes PREFIX=${redis_path} install > /dev.null && color "安装成功" 0 || color "安装失败,检查配置参数" 1
echo 'PATH=/data/redis/bin:$PATH' > /etc/profile.d/redis.sh
. /etc/profile.d/redis.sh
id redis || ( useradd -r -s /sbin/nologin redis && color "新增用户redis" 0 || color "用户redis新建失败" 1 )
mkdir /data/redis/{etc,log,data,run}
{ cp redis.conf ${redis_path}/etc/;cp sentinel.conf ${redis_path}/etc/; } && color "配置文件复制完成" 0
chown -R redis.redis ${redis_path} && color "文件权限修改完毕" 0 || color "文件权限修改失败" 1
cat >> /etc/sysctl.conf < /sys/kernel/mm/transparent_hugepage/enabled && color "关闭THP(透明大页)" 0 || "关闭THP失败" 1
echo 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' >> /etc/rc.d/rc.local
chmod +x /etc/rc.d/rc.local
[ -e /lib/systemd/system/[email protected] ] && rm -rf /lib/systemd/system/[email protected]
cat > /lib/systemd/system/[email protected] <> /data/redis/etc/redis.conf && color "redis.conf配置文件修改完毕!" 0
systemctl restart redis@redis && color "服务重启完成!" 0
break
;;
n|no)
break
;;
*)
inputerror
continue
;;
esac
done
}
function sentinel_config {
read -p "输入主节点IP地址:" masterip
if [ -z ${masterip} ];then
while [ -z ${masterip} ];do
read -p "请输入主节点IP地址:" masterip
done
fi
sed -i.bak -r -e '/#\s+bind\s+127.0.0.1/ibind 0.0.0.0' \
-e 's|^daemonize.*|daemonize yes|' \
-e 's|^logfile.*|logfile '${redis_path}'/log/redis-sentinel.log|' \
-e 's|^sentinel\smonitor\smymaster.*|sentinel monitor mymaster '${masterip}' 6379 2|' \
-e 's|^sentinel\sdown-after-milliseconds.*|sentinel down-after-milliseconds mymaster 3000|' \
${redis_path}/etc/sentinel.conf && color "sentinel配置文件修改成功!" 0
cat > /lib/systemd/system/redis-sentinel.service <
模拟master故障
正常状态下可以看到117是master,下挂两台slave
[root@centos8mini ~]# redis-cli info replication
# Replication
role:master
connected_slaves:2
slave0:ip=192.168.32.121,port=6379,state=online,offset=40797,lag=1
slave1:ip=192.168.32.123,port=6379,state=online,offset=40797,lag=1
master_failover_state:no-failover
master_replid:eac897012d5f8c9e0b884b697fa3ac6b02707613
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:40940
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:40940
模拟master故障,stop redis服务
[root@centos8mini ~]# cat /data/redis/log/redis-sentinel.log
14655:X 08 Feb 2022 17:47:24.541 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
14655:X 08 Feb 2022 17:47:24.541 # Redis version=6.2.6, bits=64, commit=00000000, modified=0, pid=14655, just started
14655:X 08 Feb 2022 17:47:24.541 # Configuration loaded
14655:X 08 Feb 2022 17:47:24.542 * Increased maximum number of open files to 10032 (it was originally set to 1024).
14655:X 08 Feb 2022 17:47:24.542 * monotonic clock: POSIX clock_gettime
14655:X 08 Feb 2022 17:47:24.543 * Running mode=sentinel, port=26379.
14655:X 08 Feb 2022 17:47:24.544 # Sentinel ID is 2dd2794641405dfb5b747853fc9b4f1610caa887
14655:X 08 Feb 2022 17:47:24.544 # +monitor master mymaster 192.168.32.117 6379 quorum 2
14655:X 08 Feb 2022 17:47:24.546 * +slave slave 192.168.32.121:6379 192.168.32.121 6379 @ mymaster 192.168.32.117 6379
14655:X 08 Feb 2022 17:47:24.550 * +slave slave 192.168.32.123:6379 192.168.32.123 6379 @ mymaster 192.168.32.117 6379
14655:X 08 Feb 2022 17:47:25.773 * +sentinel sentinel b4b649e2058e2f3323294833d065ba1c70438e28 192.168.32.123 26379 @ mymaster 192.168.32.117 6379
14655:X 08 Feb 2022 17:47:25.794 * +sentinel sentinel b6b4ffd63334c256f82c638c5487f17c47f31729 192.168.32.121 26379 @ mymaster 192.168.32.117 6379
14655:X 08 Feb 2022 17:50:22.917 # +sdown master mymaster 192.168.32.117 6379
14655:X 08 Feb 2022 17:50:22.971 # +odown master mymaster 192.168.32.117 6379 #quorum 3/2
14655:X 08 Feb 2022 17:50:22.971 # +new-epoch 1
14655:X 08 Feb 2022 17:50:22.971 # +try-failover master mymaster 192.168.32.117 6379
14655:X 08 Feb 2022 17:50:22.975 # +vote-for-leader 2dd2794641405dfb5b747853fc9b4f1610caa887 1
14655:X 08 Feb 2022 17:50:22.982 # b4b649e2058e2f3323294833d065ba1c70438e28 voted for 2dd2794641405dfb5b747853fc9b4f1610caa887 1
14655:X 08 Feb 2022 17:50:22.983 # b6b4ffd63334c256f82c638c5487f17c47f31729 voted for 2dd2794641405dfb5b747853fc9b4f1610caa887 1
#sentinel选举117作为leader
14655:X 08 Feb 2022 17:50:23.059 # +elected-leader master mymaster 192.168.32.117 6379
14655:X 08 Feb 2022 17:50:23.059 # +failover-state-select-slave master mymaster 192.168.32.117 6379
14655:X 08 Feb 2022 17:50:23.153 # +selected-slave slave 192.168.32.123:6379 192.168.32.123 6379 @ mymaster 192.168.32.117 6379
14655:X 08 Feb 2022 17:50:23.153 * +failover-state-send-slaveof-noone slave 192.168.32.123:6379 192.168.32.123 6379 @ mymaster 192.168.32.117 6379
14655:X 08 Feb 2022 17:50:23.242 * +failover-state-wait-promotion slave 192.168.32.123:6379 192.168.32.123 6379 @ mymaster 192.168.32.117 6379
14655:X 08 Feb 2022 17:50:24.036 # +promoted-slave slave 192.168.32.123:6379 192.168.32.123 6379 @ mymaster 192.168.32.117 6379
14655:X 08 Feb 2022 17:50:24.036 # +failover-state-reconf-slaves master mymaster 192.168.32.117 6379
14655:X 08 Feb 2022 17:50:24.097 * +slave-reconf-sent slave 192.168.32.121:6379 192.168.32.121 6379 @ mymaster 192.168.32.117 6379
14655:X 08 Feb 2022 17:50:25.043 * +slave-reconf-inprog slave 192.168.32.121:6379 192.168.32.121 6379 @ mymaster 192.168.32.117 6379
14655:X 08 Feb 2022 17:50:25.043 * +slave-reconf-done slave 192.168.32.121:6379 192.168.32.121 6379 @ mymaster 192.168.32.117 6379
14655:X 08 Feb 2022 17:50:25.094 # -odown master mymaster 192.168.32.117 6379
14655:X 08 Feb 2022 17:50:25.094 # +failover-end master mymaster 192.168.32.117 6379
#master发生了切换,从32.117切换到了32.123
14655:X 08 Feb 2022 17:50:25.094 # +switch-master mymaster 192.168.32.117 6379 192.168.32.123 6379
14655:X 08 Feb 2022 17:50:25.095 * +slave slave 192.168.32.121:6379 192.168.32.121 6379 @ mymaster 192.168.32.123 6379
14655:X 08 Feb 2022 17:50:25.095 * +slave slave 192.168.32.117:6379 192.168.32.117 6379 @ mymaster 192.168.32.123 6379
14655:X 08 Feb 2022 17:50:28.143 # +sdown slave 192.168.32.117:6379 192.168.32.117 6379 @ mymaster 192.168.32.123 6379
在123上查看,已经成为了master
[root@centos8mini log]# redis-cli info replication
# Replication
role:master
connected_slaves:1
slave0:ip=192.168.32.121,port=6379,state=online,offset=150486,lag=1
master_failover_state:no-failover
master_replid:ba3c099830aeb8dc6348f663f74cb6cdd73806da
master_replid2:eac897012d5f8c9e0b884b697fa3ac6b02707613
master_repl_offset:150486
second_repl_offset:65405
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:3095
repl_backlog_histlen:147392
121依然是slave,但是指向123
[root@centos8mini bin]# redis-cli info replication
# Replication
role:slave
master_host:192.168.32.123
master_port:6379
master_link_status:up
master_last_io_seconds_ago:1
master_sync_in_progress:0
slave_read_repl_offset:156534
slave_repl_offset:156534
slave_priority:100
slave_read_only:1
replica_announced:1
connected_slaves:0
master_failover_state:no-failover
master_replid:ba3c099830aeb8dc6348f663f74cb6cdd73806da
master_replid2:eac897012d5f8c9e0b884b697fa3ac6b02707613
master_repl_offset:156534
second_repl_offset:65405
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:156534