美女同事找我解决一个问题,说Tomcat启动很慢。开始我觉得是程序写的问题,因此把webapps下全部程序都删除掉。(只保留Tomcat自带)灵异的事情发生了,Tomcat中止在——java
我查看了一下进程,Tomcat所在的JVM进程已经被启动了因此能够排除是JVM退出引发的问题。那么问题真的就是JVM由于某种缘由被阻塞了。web
问题比较棘手,我排除了CPU、内存不足引发的问题;排除了硬盘空间不足引发的问题;我甚至去观察了网络I/O、硬盘I/O状况,都很是正常。程序被阻塞通常来讲必定是要等待某个资源,而如今的状况是全部资源都充足,因此我几乎想不到是什么问题引发的。算法
我开始怀疑是KVM Hypervisor虚拟化的问题(用的是虚拟机)我改变了策略在VMWare开了两台虚拟机上直接下载Tomcat启动。其中一台很快启动,另外一台竟然也被阻塞,问题被重现了。shell
眼看要在美女面前丢脸,我光辉伟岸的形象要荡然无存。这种状况下我不能去“撸”代码吧?何况Tomcat那么多人用,真有这么明显的Bug早就炸开锅了。(Tomcat仍是很靠谱的不像xxxxStack那么狗屎)仔细想一想我须要找到Tomcat中止在了哪里?代码里发生了什么事情,可是我又不可能去撸代码。迫不得已的状况下我决定试一下 strace,这是一个跟踪系统调用(System Call)的工具,不管是Java仍是Pyhton不少资源申请都会变成都会变成System Call。(好比打开文件、新建线程、读写数据、等待I/O)经过这个工具我至少能够知道Tomcat是中止在哪一个System Call上的,这样能够方便我推断出问题的缘由。apache
strace -f -o strace.out ./catalina.sh run
strace有不少参数,我用了二个参数tomcat
-f 跟踪fork的子进程,通俗的说会跟踪全部线程的系统调用安全
-o把内容输出到文件网络
其余参数请自行搜索架构
下面分析strace.out文件,分析的方法是从下往上(被阻塞的地方确定是在最后咯)。首先咱们须要去掉Tomcat中止引发的System Call,它们不是咱们须要的。从后往前搜索找到SIGINTapp
红色部分以上就是引发阻塞的系统调用了,上面有一大堆一大堆的futex的调用,它是Linux中的一种轻量级的同步方法,因此咱们能够判断出最上面确定是有某个System Call就是阻塞的真正元凶。跳过全部的futex:
这个read就是引发后面一串futex的真正缘由,strace很是聪明它不只仅给出了System Call还给出了传递的参数和返回值,read读取的是51号文件句柄,没有返回成功(unfinished)。
顺着这条路,咱们看一下51号文件句柄是什么
/dev/random是Linux下的随机函数生成器,读取它至关于生成随机数字。搜索它,第一个是wiki
至此彷佛一切真相大白了,/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都支持(i三、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,会发现长时间很长时间的等待