Tomcat 9.0.26 高併發場景下DeadLock問題排查與修復
本文首發於 vivo網際網路技術 微信公眾號
連結: https://mp.weixin.qq.com/s/-OcCDI4L5GR8vVXSYhXJ7w
作者:黃衛兵、陳錦霞
一、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”
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上修復的驗證
1.5 結果驗證
使用 提供修復後程式碼,重新打包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版本。
二、相關連結與參考
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69912579/viewspace-2673081/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 高併發場景下的快取有哪些常見的問題?快取
- 運維常見軟體問題排查與修復運維
- MySQL 在併發場景下的問題及解決思路MySql
- Redis+Lua解決高併發場景搶購秒殺問題Redis
- 高併發場景下JVM調優實踐之路JVM
- 618 Tech Talk|高併發場景下的資料訪問速度如何保障?
- 【高併發】面試官:講講高併發場景下如何優化加鎖方式?面試優化
- Tomcat 高併發之道與效能調優Tomcat
- 【高併發】高併發環境下如何優化Tomcat效能?看完我懂了!優化Tomcat
- 高併發場景下如何優化伺服器的效能?優化伺服器
- RocketMQ實戰--高併發秒殺場景MQ
- 高併發業務場景下的秒殺解決方案 (初探)
- 線上併發事務死鎖問題排查
- 專案中Spring事務失效的場景問題排查Spring
- 【高併發】高併發環境下如何防止Tomcat記憶體溢位?看完我懂了!!Tomcat記憶體溢位
- C++高併發場景下讀多寫少的解決方案C++
- Java服務.問題排查.問題復現Java
- 《重學Java高併發》Sempahore的使用場景與常見誤區Java
- SDK與問題排查
- Serverless 場景排查問題利器:函式例項命令列操作Server函式命令列
- 【高併發】億級流量場景下如何為HTTP介面限流?看完我懂了!!HTTP
- [分散式][高併發]訊息佇列的使用場景、概念、常見問題及解決方案分散式佇列
- PHP+Redis解決高併發下商品超賣問題PHPRedis
- 修復UEFI模式下Manjaro Linux啟動問題模式JARLinux
- 請求合併與拆分在併發場景中應用
- GRPC使用問題修復RPC
- mysql 高併發 select update 併發更新問題解決方案MySql
- .NET WebSocket高併發通訊阻塞問題Web
- 高併發快取面臨的問題快取
- Tomcat高階配置(應用場景總結及示例)Tomcat
- 記一次Git分支合併引起的問題和修復Git
- Apache Tomcat如何高併發處理請求ApacheTomcat
- [分散式]高併發案例---庫存超發問題分散式
- 高併發下資料冪等問題的9種解決方案
- 併發場景下資料寫入功能的實現
- 創新推出 | Serverless 場景排查問題利器:函式例項命令列操作Server函式命令列
- Ubuntu1804下k8s-CoreDNS佔CPU高問題排查UbuntuK8SDNS
- 記錄一次亂碼問題修復經歷!排查修復Poi-tl使用HttpServletResponse匯出Word亂碼問題HTTPServlet