Tomcat 9.0.26 高并发场景下DeadLock问题排查与修复

简介: 本文主要针对deadlock问题进行分析以及通过使用Apache提供的新patch进行验证。

本文首发于 vivo互联网技术 微信公众号 
作者:黄卫兵、陈锦霞

一、Tomcat容器 9.0.26 版本 Deadlock 问题

1.1 问题现象

1.1.1  发生 Deadlock 的背景

某接口/get.do压测,3分钟后,成功事务数TPS由1W骤降至0。

1.1.2  Tomcat服务器出现大量的CLOSE_WAIT

被压测服务器,出现TCP CLOSE_WAIT状态个数在200~2W左右。

1.2 初步定位:线程堆栈信息入手

通过jstack打印Tomcat堆栈信息,发现“Found 1 deadlock”

Found one Java-level deadlock:
=============================
"http-nio-8080-exec-409":
waiting to lock monitor 0x00007f064805aa78 (object 0x00000006c0ebf148, a java.util.HashSet),
which is held by "http-nio-8080-ClientPoller"
"http-nio-8080-ClientPoller":
waiting to lock monitor 0x00007f05e8061058 (object 0x00000007bfe40a70, a java.lang.Object),
which is held by "http-nio-8080-exec-205"
"http-nio-8080-exec-205":
waiting to lock monitor 0x00007f0614018448 (object 0x00000006c0e8e088, a java.util.HashSet),
which is held by "http-nio-8080-BlockPoller"
"http-nio-8080-BlockPoller":
waiting to lock monitor 0x0000000001ed06e8 (object 0x00000007bfe110f8, a java.lang.Object),
which is held by "http-nio-8080-exec-380"
"http-nio-8080-exec-380":
waiting to lock monitor 0x00007f064805aa78 (object 0x00000006c0ebf148, a java.util.HashSet),
which is held by "http-nio-8080-ClientPoller"

1.2.1  快速修复方案

内部讨论后,认为当前Tomcat版本可能有Bug。不影响项目进度,简单修改方案把SpringBoot 使用的Tomcat 9.0.26 降级到Tomcat 8。降级后再次压测,没有发现问题。基本上可以确定Tomcat 9.0.26 应该是存在 Deadlock 问题。

1.3  问题进一步跟踪

1.3.1  向Apache社区的反馈

为了确认问题,我们试着给Tomcat提交Bug反馈。

从堆栈信息来看,是3类线程5个线程由于加锁的顺序不致,从而相互等待发生了死锁。图形化上面加锁的过程如下图。

1.4 问题原因分析

明确了死锁的过程,但是哪个环节出了问题呢。这就需要深入到源码层去定位问题。首先需要下载OpenJDK 源码,然后是Tomcat 9.0.26 的源码。根据堆栈信息,定位到相应的代码位置。我们理出如下图Tomcat 9.0.26死锁流程说明。

要比较好的理解上图,需要对于NIO有一定的了解。在Tomcat中NIO主要是理解NIO Endpoint。

Poller是对于Selector的一个封装,而线程名为exec-xx的执行线程是Channel的封装。在NIO中Channel注册到Selector然后通过SelectionKey来记录对应关系。到此,主角都上场了。

Poller的run方法作为后台线程一直在轮询(select)准备好的SelectionKey,在轮询的时候也顺便需要把cancelledKey中的SelectionKey给反注册。执行线程EXEC-XX在处理时会先判断连接的状态,比如失败、异常等情况会调用Channel的close方法去关闭连接。

而Channel的close实际只是把SelectionKey加入到cancelledKey。两者都需要先锁定,但锁定的顺序不一致,从而导致死锁。

1.4.1  与Tomcat开发者的交流

在提交Bug后,很快得到了Remy Maucherat的回复,首先他提到这个NIO内部的死锁。然后我们提到NIO内部的死锁是由于Poller.run和Poller.canceledKey在并发时导到的。

Remy Maucherat很快就进行了修复,主要是把Poller.canceledKey中close移到了finally中去执行,也就是先让Poller.run获得锁。

在得到修复后,我们使用替换后的代码进行了再次压测,死锁问题没有出现了。Remy Maucherat同时提到在最新的OpenJDK中相关问题的修复,但只会出现在jdk 11和14版本。

沟通中的详情见下图。

1.4.2  Github上修复的验证

https://github.com/apache/tomcat/commit/9b1a8b67bffe462fc745b19e15ed59c37e2e1dcf

1.5 结果验证

使用 https://github.com/apache/tomcat/commit/9b1a8b67bffe462fc745b19e15ed59c37e2e1dcf 提供修复后代码,重新打包tomcat-embed-core.jar 替换9.X.XX的再次压测,TPS平稳在1.5W左右。

到此问题基本是定位清楚,并得到了修复。Remy Maucherat也回复到“The fix will be in Tomcat 9.0.31+”。

目前Tomcat 最新版本是Tomcat 9.0.30,还需要耐心等待31版本更新。建议使用Tomcat 8版本。

二、相关链接与参考

  1. OpenJdk源码下载

  2. Tomcat 源码

  3. 来自阿里云溪社区:断网故障时Mtop触发Tomcat高并发场景下的BUG排查和修复

  4. 深度解读Tomcat中的NIO模型 

相关实践学习
通过性能测试PTS对云服务器ECS进行规格选择与性能压测
本文为您介绍如何利用性能测试PTS对云服务器ECS进行规格选择与性能压测。
目录
相关文章
|
2月前
|
应用服务中间件 Linux 开发者
用的到linux-tomcat端口占用排查-Day5
通过本文的介绍,详细讲解了在CentOS 8系统上排查和解决Tomcat端口占用问题的方法。从使用 `netstat`、`lsof`和 `ss`命令检查端口占用情况,到使用 `ps`和 `top`命令查找和停止占用端口的进程,再到修改Tomcat端口配置,最后介绍了自动化脚本的方法。希望本文能帮助系统管理员和开发者有效地解决端口占用问题,确保Tomcat服务器的正常运行。
64 11
|
4月前
|
缓存 NoSQL 架构师
Redis批量查询的四种技巧,应对高并发场景的利器!
在高并发场景下,巧妙地利用缓存批量查询技巧能够显著提高系统性能。 在笔者看来,熟练掌握细粒度的缓存使用是每位架构师必备的技能。因此,在本文中,我们将深入探讨 Redis 中批量查询的一些技巧,希望能够给你带来一些启发。
319 23
Redis批量查询的四种技巧,应对高并发场景的利器!
|
4月前
|
弹性计算 NoSQL 关系型数据库
高并发交易场景下业务系统性能不足?体验构建高性能秒杀系统!完成任务可领取锦鲤抱枕!
高并发交易场景下业务系统性能不足?体验构建高性能秒杀系统!完成任务可领取锦鲤抱枕!
|
5月前
|
缓存 NoSQL Java
高并发场景秒杀抢购超卖Bug实战重现
在电商平台的秒杀活动中,高并发场景下的抢购超卖Bug是一个常见且棘手的问题。一旦处理不当,不仅会引发用户投诉,还会对商家的信誉和利益造成严重损害。本文将详细介绍秒杀抢购超卖Bug的背景历史、业务场景、底层原理以及Java代码实现,旨在帮助开发者更好地理解和解决这一问题。
178 12
|
7月前
|
网络协议 Java 应用服务中间件
深入浅出Tomcat网络通信的高并发处理机制
【10月更文挑战第3天】本文详细解析了Tomcat在处理高并发网络请求时的机制,重点关注了其三种不同的IO模型:NioEndPoint、Nio2EndPoint 和 AprEndPoint。NioEndPoint 采用多路复用模型,通过 Acceptor 接收连接、Poller 监听事件及 Executor 处理请求;Nio2EndPoint 则使用 AIO 异步模型,通过回调函数处理连接和数据就绪事件;AprEndPoint 通过 JNI 调用本地库实现高性能,但已在 Tomcat 10 中弃用
深入浅出Tomcat网络通信的高并发处理机制
|
6月前
|
缓存 监控 Java
Java 线程池在高并发场景下有哪些优势和潜在问题?
Java 线程池在高并发场景下有哪些优势和潜在问题?
127 2
|
7月前
|
NoSQL Java Redis
京东双十一高并发场景下的分布式锁性能优化
【10月更文挑战第20天】在电商领域,尤其是像京东双十一这样的大促活动,系统需要处理极高的并发请求。这些请求往往涉及库存的查询和更新,如果处理不当,很容易出现库存超卖、数据不一致等问题。
154 1
|
7月前
|
存储 缓存 NoSQL
大数据-38 Redis 高并发下的分布式缓存 Redis简介 缓存场景 读写模式 旁路模式 穿透模式 缓存模式 基本概念等
大数据-38 Redis 高并发下的分布式缓存 Redis简介 缓存场景 读写模式 旁路模式 穿透模式 缓存模式 基本概念等
191 4
|
7月前
|
Java Linux
【网络】高并发场景处理:线程池和IO多路复用
【网络】高并发场景处理:线程池和IO多路复用
195 2
|
9月前
|
NoSQL 关系型数据库 MySQL
排行榜系统设计:高并发场景下的最佳实践
本文由技术分享者小米带来,详细介绍了如何设计一个高效、稳定且易扩展的排行榜系统。内容涵盖项目背景、技术选型、数据结构设计、基本操作实现、分页显示、持久化与数据恢复,以及高并发下的性能优化策略。通过Redis与MySQL的结合,确保了排行榜的实时性和可靠性。适合对排行榜设计感兴趣的技术人员参考学习。
1221 7
排行榜系统设计:高并发场景下的最佳实践