tomcat启动很慢很慢,长达122873ms

本文详细描述了作者在启动Tomcat时遇到的长时间延迟问题,通过strace工具定位到问题根源在于/dev/random导致的阻塞。了解到Tomcat在启动时使用随机数生成器来提高安全性,而/Linux下的/dev/random在熵池不足时会阻塞。解决方案包括:1) 修改Tomcat或JRE配置使用/dev/urandom;2) 增大/dev/random熵池,如使用rngd服务。作者推荐使用第三种方法,即通过rngd服务提高熵池生成速度,以从根本上解决问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

tomcat在启动后,查看日志。发现tomcat的启动非常慢 如下图
这里写图片描述

我查看了一下进程,Tomcat所在的JVM进程已经被启动了所以可以排除是JVM退出引起的问题。那么问题真的就是JVM因为某种原因被阻塞了。
● 分析
问题比较棘手,我排除了CPU、内存不足引起的问题;排除了硬盘空间不足引起的问题;我甚至去观察了网络I/O、硬盘I/O情况,都非常正常。程序被阻塞一般来说一定是要等待某个资源,而现在的情况是所有资源都充足,所以我几乎想不到是什么问题引起的。我开始怀疑是KVM Hypervisor虚拟化的问题(用的是虚拟机)我改变了策略在VMWare开了两台虚拟机上直接下载Tomcat启动。其中一台很快启动,另一台居然也被阻塞,问题被重现了。况且Tomcat那么多人用,真有这么明显的Bug早就炸开锅了。无可奈何的情况下我决定试一下 strace,这是一个跟踪系统调用(System Call)的工具,无论是Java还是Pyhton很多资源申请都会变成都会变成System Call。(比如打开文件、新建线程、读写数据、等待I/O)通过这个工具我至少可以知道Tomcat是停止在哪个System Call上的,这样可以方便我推断出问题的原因。
strace -f -o strace.out ./catalina.sh run
strace有很多参数,我用了二个参数
● -f 跟踪fork的子进程,通俗的说会跟踪所有线程的系统调用
● -o把内容输出到文件
其他参数请自行搜索下面分析strace.out文件,分析的方法是从下往上(被阻塞的地方肯定是在最后咯)。首先我们需要去掉Tomcat停止引起的System Call,它们不是我们需要的。从后往前搜索找到SIGINT
这里写图片描述

红色部分以上就是引起阻塞的系统调用了,上面有一大堆一大堆的futex的调用,它是Linux中的一种轻量级的同步方法,所以我们可以判断出最上面肯定是有某个System Call就是阻塞的真正元凶。跳过所有的futex:
这里写图片描述
这个read就是引起后面一串futex的真正原因,strace非常聪明它不仅仅给出了System Call还给出了传递的参数和返回值,read读取的是51号文件句柄,没有返回成功(unfinished)。顺着这条路,我们看一下51号文件句柄是什么
这里写图片描述
/dev/random是Linux下的随机函数生成器

Linux中的随机数可以从两个特殊的文件中产生,一个是/dev/urandom.另外一个是/dev/random。他们产生随机数的原理是利用当前系统的熵池来计算出固定一定数量的随机比特,然后将这些比特作为字节流返回。熵池就是当前系统的环境噪音,熵指的是一个系统的混乱程度,系统噪音可以通过很多参数来评估,如内存的使用,文件的使用量,不同类型的进程数量等等。如果当前环境噪音变化的不是很剧烈或者当前环境噪音很小,比如刚开机的时候,而当前需要大量的随机比特,这时产生的随机数的随机效果就不是很好了。

至此似乎一切真相大白了,/dev/random会根据噪音产生随机数,如果噪音不够它就会阻塞。Linux是通过I/O,键盘终端、内存使用量、CPU利用率等方式来收集噪音的,如果噪音不够生成随机数的时候就会被阻塞。
● 深入分析
如果用Tomcat /dev/random作为关键字基本上就能够回答我们的疑惑了。Tocmat的Session ID是通过SHA1算法计算得到的,计算Session ID的时候必须有一个密钥。为了提高安全性Tomcat在启动的时候回通过随机生成一个密钥。在 http://wiki.apache.org/tomcat/HowTo/FasterStartUp (Entropy Source部分)有一段解释。stackoverflow上面也有一大批这方面的说明,所以这里就不再多做介绍。明白了问题的原因解决起来就非常简单了——替换/dev/random为/dev/unrandom,用伪随机函数生成器(/dev/urandom)来替代随机函数生成器(/dev/random)。
● 通过修改Tomcat启动文件-Djava.security.egd=file:/dev/urandom
● 通过修改JRE中的java.security文件securerandom.source=file:/dev/urandom
当然JVM的开发者不是傻瓜,Tomcat的开发者也不是二百五。他们之所以没有选择/dev/urandom是为了提高系统的安全性,/dev/urandom并不是真正的随机行为。(其实一般情况下/dev/urandom也是足够安全的不太容易被“重复”)
● 彻底解决问题
上面介绍的两种方式都是用/dev/urandom替换/dev/random,其实还有第三种方式——增大/dev/random的熵池。问题的原因是由于熵池不够大,所以增大它是最彻底的方法。通过cat /proc/sys/kernel/random/entropy_avail我们可以查看现在的熵池大小;我们需要找到一种方式来提高这个值就行了。如果你的CPU带有DRNG特性,可以充分利用硬件来提高熵池产生的速度 。通过cat /proc/cpuinfo | grep rdrand可以查看自己的CPU是否支持,一般来说Intel的Ivy_Bridge架构的CPU都支持(i3、i5需要注意是否采用该种架构,i7和xeon基本上都支持);AMD的CPU在2015年以后生成的都支持。(如果你是虚拟机需要开启额外的参数)。如果你的硬件不支持,也没有关系,我们可以让/dev/unrandom来做“熵源”。以Centos7为例,
● yum install rngd-tools安装rngd服务(熵服务)
● systemctl start rngd启动服务
如果你的CPU不支持DRNG特性或者像我一样使用虚拟机,可以使用/dev/unrandom来模拟。
● cp /usr/lib/systemd/system/rngd.service /etc/systemd/system
● 编辑/etc/systemd/system/rngd.service service小结,ExecStart=/sbin/rngd -f -r /dev/urandom
● systemctl daemon-reload重新载入服务
● systemctl restart rngd重启服务
经过上面的修改,我们再观察/proc/sys/kernel/random/entropy_avail基本上在3000左右。我们可以测试一下随机数的生成速度
● watch -n 1 cat /proc/sys/kernel/random/entropy_avail观察这个值
● 新打开一个shell,用dd命令测试随机数。dd if=/dev/random of=random.dat count=40960
[root@localhost bin]# dd if=/dev/random of=random.dat count=40960
记录了0+40960 的读入
记录了6004+1 的写出
3074362字节(3.1 MB)已复制,5.01017 秒,614 kB/秒
5秒产生了40960个随机数,/proc/sys/kernel/random/entropy_avail会有剧烈的变化,所有随机数产生之后它又会保持在3000左右。
● 选择哪种解决方法
个人建议选择第三种方式,熵池不仅仅Tomcat用,Linux下的所有应用程序产生随机数都会用到这个,所以不仅仅是Tomcat可能被阻塞。如果你搜索会发现Apache、Nginx、OpenSSL都被这个问题坑过。如果我们通过修改Java的配置来解决这个问题其实只是解决Java应用程序的问题,只能是治标不治本。根治的方法应该是通过rngd提高随机数生成的速度。
● 总结
经验不是经历。用别人的经验解决一个问题不难,难的是自己从头走一遍这条路,更加难的是推翻前人的经验对一个问题能够有自己的看法和领悟。这个案例加深了我对strace的理解,对于空中加油这种类型的系统调试有了自己的经验;通过对原因的深入分析我找到了更好的办法。这就是康德精神——思考、批判、理性。
● 如何重现故障
可以很容易的重现文章中描述的故障
● systemctl stop rngd 停止rngd服务(如果你有启动rngd)
● 查看当前熵池的大小cat /proc/sys/kernel/random/entropy_avail
● head -c1024 /dev/random,强制消费1024个随机数,系统会长时间没有反应。直接ctrl+c
● 再次查看熵池的大小cat /proc/sys/kernel/random/entropy_avail,保证它的大小在尽可能的小
● 启动tomcat,会发现长时间很长时间的等待

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值