彻底找到 Tomcat 启动速度慢的元凶

发布于 2024-08-22 15:34:48 字数 4869 浏览 19 评论 0

问题现象

美女同事找我解决一个问题,说 Tomcat 启动很慢。开始我以为是程序写的问题,所以把 webapps 下所有程序都删除掉。(只保留 Tomcat 自带)灵异的事情发生了,Tomcat 停止在—— 我查看了一下进程,Tomcat 所在的 JVM 进程已经被启动了所以可以排除是 JVM 退出引起的问题。那么问题真的就是 JVM 因为某种原因被 阻塞 了。

分析

问题比较棘手,我排除了 CPU、内存不足引起的问题;排除了硬盘空间不足引起的问题;我甚至去观察了网络 I/O、硬盘 I/O 情况,都非常正常。程序被 阻塞 一般来说一定是要等待某个资源,而现在的情况是所有资源都充足,所以我几乎想不到是什么问题引起的。 我开始怀疑是 KVM Hypervisor 虚拟化的问题(用的是虚拟机)我改变了策略在 VMWare 开了两台虚拟机上直接下载 Tomcat 启动。其中一台很快启动,另一台居然也被 阻塞 ,问题被重现了。 眼看要在美女面前丢脸,我光辉伟岸的形象要荡然无存。这种情况下我不能去 代码吧?

况且 Tomcat 那么多人用,真有这么明显的 Bug 早就炸开锅了。(Tomcat 还是很靠谱的不像 xxxxStack 那么狗屎)仔细想想我需要找到 Tomcat 停止在了哪里?代码里发生了什么事情,但是我又不可能去撸代码。无可奈何的情况下我决定试一下 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 下的随机函数生成器,读取它相当于生成随机数字。搜索它,第一个是 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 都支持(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,会发现长时间很长时间的等待。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

尛丟丟

暂无简介

0 文章
0 评论
24 人气
更多

推荐作者

山人契

文章 0 评论 0

ぇ气

文章 0 评论 0

linces

文章 0 评论 0

qq_gyAaBXt5

文章 0 评论 0

じ☆小静

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文