本文共 5893 字,大约阅读时间需要 19 分钟。
[toc]
在线上环境中,我们经常会遇到类似的问题,就是tomcat 启动比较慢,查看内存和cpu,io都是正常的,但是启动很慢,有的时候长达几分钟,这到底是什么原因导致的。
### 1.1 tomcat 获取随机值阻塞
tomcat的启动需要产生session id,这个产生需要通过java.security.SecureRandom
生成随机数来实现,随机数算法使用的是”SHA1PRNG”,但这个算法依赖于操作系统的提供的随机数据,在linux系统中,这个值又依赖于/dev/random 和/dev/urandom
/dev/random :阻塞型,读取它就会产生随机数据,但该数据取决于熵池噪声,当熵池空了,对/dev/random 的读操作也将会被阻塞。/dev/urandom: 非阻塞的随机数产生器,它会重复使用熵池中的数据以产生伪随机数据。这表示对/dev/urandom的读取操作不会产生阻塞,但其输出的熵可能小于/dev/random的。它可以作为生成较低强度密码的伪随机数生成器,不建议用于生成高强度长期密码。
我们通过查看java.security 文件,(我的java版本是1.8.0_131) 发现依赖的是/dev/random
tomcat 启动产生session id 最终依赖的是/dev/random ,/dev/random 又依赖于熵池,
对于熵池,百度百科这样写到
Linux内核采用熵来描述数据的随机性。熵(entropy)是描述系统混乱无序程度的物理量,一个系统的熵越大则说明该系统的有序性越差,即不确定性越大。在信息学中,熵被用来表征一个符号或系统的不确定性,熵越大,表明系统所含有用信息量越少,不确定度越大。计算机本身是可预测的系统,因此,用计算机算法不可能产生真正的随机数。但是机器的环境中充满了各种各样的噪声,如硬件设备发生中断的时间,用户点击鼠标的时间间隔等是完全随机的,事先无法预测。Linux内核实现的随机数产生器正是利用系统中的这些随机噪声来产生高质量随机数序列。内核维护了一个熵池用来收集来自设备驱动程序和其它来源的环境噪音。理论上,熵池中的数据是完全随机的,可以实现产生真随机数序列。为跟踪熵池中数据的随机性,内核在将数据加入池的时候将估算数据的随机性,这个过程称作熵估算。熵估算值描述池中包含的随机数位数,其值越大表示池中数据的随机性越好。
那么如何查看熵池 的大小,文件 /proc/sys/kernel/random/entropy_avail 保存着 熵池的大小。/proc/sys/kernel/random/poolsize 保存着熵池的最大容量,单位都是bit。
[root@haha cwd]# cat /proc/sys/kernel/random/entropy_avail146
总结 tomcat 启动慢的原因是随机数产生遭到阻塞,遭到阻塞的原因是 熵池大小 。
解决方法:
1. 更换产生随机数的源,(也是tomcat的官方文档的启动比较慢的解决办法) 2. 增大熵池 的值
1 . 更换产生随机数的源
因为/dev/urandom 是非阻塞的随机数产生器,所以我们可以从这边获取,但是生产的随机数的随机性比较低。我们可以在 我们的tomcat启动脚本(catalina.sh)里面添加
JAVA_OPTS="$JAVA_OPTS -Djava.security.egd=file:/dev/./urandom"
或者是更改java的java.security 文件,将securerandom.source=file:/dev/random
securerandom.source=file:/dev/./urandom
注意一下,为什么我们这里使用的路径是"/dev/./urandom",而不是 "/dev/urandom",是因为在java 8之前的版本设置了/dev/urandom ,但是实际还是使用/dev/random,设置为"/dev/./urandom"才能正常使用 "/dev/urandom" , 这个bug在java8版本已经修复了,如果你是java7版本的话,需要按照上面设置,java8的话可以不用加 "./"。
2 .增大熵池 的值
要增大熵池 的值首先得你的cpu支持DRNG 特性, 如何查看我们的服务器的是否支持DRNG特性?
cat /proc/cpuinfo | grep rdrand
如果不支持的话,那么就只能通过上面的第一种方法来解决了
安装rngd服务()
yum -y install rng-toolssystemctl enable rngdsystemctl start rngd
然后我们进行查看我们的熵池 的值,会发现变大了
cat /proc/sys/kernel/random/entropy_avail
然后我们启动tomcat 会发现启动速度快很多。
有的时候,我们tomcat启动比较慢是因为它需要部署的web应用程序太多,但是其中有些应用程序是我们不需要的,比如在webapps下的 doc 、example、ROOT 等等,我们可以将我们不需要的webapps删除,然后再进行发布,这些不需要的web,不仅会占用我们的资源,还有可能是入侵者的入侵对象。如果我们想并行启动多个web应用程序,我们可以Host 的属性 startStopThreads 值设置大于1 ,但这也取决于我们的服务器是不是多核的。如果是多核的建议调大 startStopThreads 的值,但不超过内核数。
如果是项目比较大的话,我们使用默认的参数去启动的tomcat是很有可能内存不足的,我们需要设置JVM,将内存调整,JVM 的最大值和最小值建议是不要相差太大(最好一致.)
在启动脚本catalina.sh加上:
JAVA_OPTS='-server -Xms1024m -Xmx1024m'
具体的内存大小,根据业务调整。
以上就是解决tomcat 启动慢的问题和解决方案,可根据自己的项目情况进行使用。后面也会有一篇tomcat 调优的文章,请大家点波关注哦。
### 2.1 使用arp 连接器
tomcat 可以使用来提供更高的性能服务。Apache Portable Runtime是一个高度可移植的库,是Apache HTTP Server 2.x的核心。APR有许多用途,包含高级的io功能(sendfile,epoll,Openssl),系统级别功能(产生随机数,系统状态)和进程处理(共享内存,NT管道,unix套接字),它可以让tomcat成为通用的web服务器,让java应用作为一个完整的web服务器更加可行,而不是仅仅作为后端的技术。
总结:从系统级别来解决异步io的问题,提升性能。
apr 连接器需要自己手动安装,需要以下组件
2.1 yum安装(建议)
注意apr安装的版本
yum -y install opensslyum -y install apryum -y install apr-utilyum -y install tomcat-native
2.2 源码安装(yum和源码安装二选一)
apr源码编译 安装流程:
下载地址 :
yum -y install make gcc gcc-c++ openssl-devel libtoolcd /tmp && wget http://archive.apache.org/dist/apr/apr-1.6.5.tar.gz
解压apr 包
tar -xzf apr-1.6.5.tar.gz && cd apr-1.6.5
预编译,编译,安装
./configure --prefix=/usr/local/apr make make install
native 安装
解压native 包 (native 包在tomcat 安装包里面 bin下)
cd tomcat/bin && tar -xzf tomcat-native.tar.gz
预编译,编译,安装
cd tomcat-native*/native && ./configure --with-apr=/usr/local/apr make make install
添加环境变量
vim + /etc/profile# 添加环境变量export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/apr/lib# 加载变量source /etc/profile
2.3 配置apr 连接器
vim ./conf/server.xml# 将 Connector 属性protocol 配置成 apr(org.apache.coyote.http11.Http11AprProtocol)
默认连接器为nio,共有三大连接器:
org.apache.coyote.http11.Http11NioProtocol
非阻塞nio连接器org.apache.coyote.http11.Http11Nio2Protocol
非阻塞nio2连接器org.apache.coyote.http11.Http11AprProtocol
APR连接器重启tomcat
APR 测试·
tomcat,jvm使用默认配置进行测试,使用nio 连接器 共1000次请求100并发
在nio连接器时,我多次尝试使用150并发共1000请求去测试,发现一直成功不了,但是当我使用apr连接时,
100并发
150 并发
我们可以对比发现,在使用APR连接器在处理高并发请求的时候是有一定的优势的,能够提升web应用的处理请求能力。
属性 | 描述 | 建议设置的值 |
---|---|---|
maxThreads | tomcat能创建来处理请求的最大线程数,默认值为200 | 500 |
minProcessors | 启动时创建的线程数(最小线程数) | |
acceptCount | 指定当所有可以使用的处理请求的线程数都被使用时,可以放到队列中的请求数,,超过这个数的请求将拒绝连接 默认值为100 | 500 |
compressibleMimeType | 该值用来指定哪些文件类型的文件可以进行压缩,默认值为:text/html,text/xml,text/plain,text/css,text/javascript,application/javascript | |
compression | 开启gzip 压缩,可以接受的值是 "off"(禁用压缩),"on"(开启压缩),"force(强制压缩)","1-9"(等效于开启压缩,并且设定压缩等级),开启了压缩,也就意味着要占用更多的cpu资源 | on |
keepAliveTimeout | 指connector两个HTTP请求直接的等待时间,超过该时间没有接收到第二个HTTP请求就关闭连接,默认是使用connectionTimeout 的值,单位为毫秒 | 30000 |
processorCache | 进程缓冲器,默认值是maxThreads的值,使用好该值可以提升并发请求。 | 500 |
以上设置只是参考,建议根据自己的业务进行设置,并测试。
有很多参数tomcat 默认就给我设置好了,比如URIEncoding,connectionTimeout,这些值都有默认值的,详细见文章属性 | 描述 | 建议配置 |
---|---|---|
startStopThreads | 指Host用于启动Context的线程数,默认值为1,如果多核建议配置为核心数-1 | 2 |
unpackWARs | 默认为true,如果设置为true 表示将web应用程序war包解压,false表示直接从war文件运行。设置为false对性能有一定的影响 | true |
如果我们想并行启动多个web应用程序,我们可以Host 的属性 startStopThreads 值设置大于1 ,但这也取决于我们的服务器是不是多核的。如果是多核的建议调大 startStopThreads 的值,但不超过内核数。
如果我们使用的是自动发布的形式,也就是替换war包的形式,在tomcat安装文章中介绍了与jenkins 集成实现的,那么容易出现的一个问题就是 上一次的线程没有关闭掉,就启动了新的版本,那么上一个版本的线程还存在,也还在占用着资源,这个问题的原因有可能是代码的问题,我们可以尝试直接使用 catalina.sh 脚本stop ,你会发现stop 掉后,该tomcat 的线程还是存在的,那么出现这种情况很大一部分原因就是 在java代码中有非守护线程,也就是java代码未将线程设置为守护线程,导致了tomcat 进行stop 不掉该线程的原因。那么这种情况有可能会导致两个web应用(新老版本)都在使用,那这这个应用的定时任务可能就会执行两次,就容易导致生产事故。
这种问题,一方面会占用服务器资源。另外一方面还会容易导致生产事故,我们可以用jstack 分析下未停止的线程,并和开发区解决这个线程关闭不掉的问题。
AJP协议在tomcat中的作用就是将该服务与其它HTTP服务器集成,我们一般项目中,没有用到该连接器,所以我们可以禁用该连接器
在前面启动内存不足就谈到了
JAVA_OPTS='-server -Xms1024m -Xmx1024m'
其他的调优选项,如果你有多个connector 的话,那么你还可以通过 Executor 的属性进行调优。
转载地址:http://qfgjx.baihongyu.com/