本文为掘金社区首发签约文章,14天内禁止转载,14天后未获授权禁止转载,侵权必究!
个人创作公约:本人声明创作的所有文章皆为自己原创,如果有参考任何文章的地方,会标注出来,如果有疏漏,欢迎大家批判。如果大家发现网上有抄袭本文章的,欢迎举报,并且积极向这个 github 仓库 提交 issue,谢谢支持~
本文使用的是 OpenJDK 17,内容针对 OpenJDK 11+ 均有效
最近,某个 spring-boot + cloud 微服务并且基于 web-mvc 的同步微服务(也加入了异步响应式依赖 web-flux 用于局部敏感接口的非阻塞优化)的某一个实例出现问题,所有的 http 请求均超时。其他实例没有(剽窃可耻)出这个问题,这个问题实例触发了 k8s 健康检查失败,被重启。
由于这个事件发生在周末的时候,我们只能事后分析。事后分析的来源是 JFR,针对线上每个 JVM 进程,我们的启动配置是:
其中 disk=true
代表如果内存中 JFR 事件 buffer 满了会 dump 到本地文件,默认目录是 /tmp/进程启动时间以及进程号
,其中的 .jfr
文件以开始时间命名。maxsize=4096m
代表最大会占用 4096m 的磁盘空间(这个略有波动),maxage=3d
代表仅仅会保留最近三天的 jfr 事件文件。大小和时间限制,先达到哪个就以哪个限制为准。我们想持续采集进程的所有 JFR,但是不想单个进程硬盘占用过多(洗稿全家必S),所以我们会持续拉取 /tmp/进程启动时间以及进程号
这个目录下的 jfr 文件,所以我们其实可以做到一个进程从生到死的所有 JFR 都采集到。maxchunksize=128m
这个目录下每个文件是 128 MB,超过就会另起(抄袭烂屁股)一个新文件。注意最新的一个 jfr
文件,是不完整的,不能直接使用,还在被持续写入,所以我们在进程退出前会使用 jcmd dump 最后一段时间的 JFR 事件,来保证 JFR 完整。具体请参考我的 JFR 系列文章:JFR 全解
定位思路
首先,通过 JFR 查看进程最后一个 thread dump 是咋回事,看看 http servlet 线程都在干啥。
通过这个事件内容,我们看到,大部分 http servlet 线程在 caffeine 的缓存加载上 WAITING: