Log4j線程死鎖–案例研究

此案例研究描述了影響Weblogic Portal 10.0生產環境的Apache Log4j線程爭用問題的完整根本原因分析和解決方案。 它還將說明在開發和支持Java EE應用程序時適當的Java類加載器知識的重要性。

本文也是您提高線程轉儲分析技能和了解線程競爭條件的另一個機會。

環境規格

  • Java EE服務器:Oracle Weblogic Portal 10.0
  • 操作系統:Solaris 10
  • JDK:Oracle / Sun HotSpot JVM 1.5
  • 記錄API:Apache Log4j 1.2.15
  • RDBMS:Oracle 10g
  • 平臺類型:Web門戶

故障排除工具

  • Quest Foglight for Java(監視和警報)
  • Java VM線程轉儲(線程競爭分析)

問題概述

從我們的Weblogic Portal生產環境之一觀察到主要性能下降。 Foglight代理還發送了警報,表明Weblogic線程利用率顯著上升,達到默認上限400。

事實的收集和驗證

像往常一樣,Java EE問題調查需要收集技術事實和非技術事實,因此我們可以得出其他事實和/或就根本原因進行結論。 在采取糾正措施之前,要對以下事實進行核實,以便得出根本原因:

  • 對客戶有什么影響?
  • 受影響平臺的最近更改? 是的,最近進行的部署涉及少量內容更改以及一些Java庫更改和重構。
  • 受影響平臺最近的流量增加了嗎? 沒有
  • 自從多久以來觀察到此問題? 部署后觀察到新問題
  • 重新啟動Weblogic服務器是否可以解決問題? 否,任何重新啟動嘗試均會立即導致線程激增
  • 回滾部署更改是否解決了問題? 是

結論1:問題似乎與最近的變化有關。 但是,團隊最初無法查明根本原因。 現在,我們將在本文的其余部分進行討論。

Weblogic占用線程報告

最初的線程激增問題由Foglight報告。 如下所示,線程利用率很高(最多400個),從而導致大量待處理的客戶端請求,最終導致嚴重的性能下降。

通常,線程問題需要進行適當的線程轉儲分析,以查明線程爭用的根源。 缺乏這種關鍵的分析技能將使您無法進行根本原因分析。

對于我們的案例研究,使用簡單的Solaris OS命令kill -3 <Java PID>從Weblogic服務器生成了一些線程轉儲快照。 然后從Weblogic標準輸出日志文件中提取線程轉儲數據。

線程轉儲分析

分析的第一步是對所有卡住的線程進行快速掃描,并找出問題的“模式”。 我們發現有250個線程卡在以下執行路徑中:

"[ACTIVE] ExecuteThread: '20' for queue: 'weblogic.kernel.Default (self-tuning)'" daemon prio=10 tid=0x03c4fc38 nid=0xe6 waiting for monitor entry [0x3f99e000..0x3f99f970]at org.apache.log4j.Category.callAppenders(Category.java:186)- waiting to lock <0x8b3c4c68> (a org.apache.log4j.spi.RootCategory)at org.apache.log4j.Category.forcedLog(Category.java:372)at org.apache.log4j.Category.log(Category.java:864)at org.apache.commons.logging.impl.Log4JLogger.debug(Log4JLogger.java:110)at org.apache.beehive.netui.util.logging.Logger.debug(Logger.java:119)at org.apache.beehive.netui.pageflow.DefaultPageFlowEventReporter.beginPageRequest(DefaultPageFlowEventReporter.java:164)at com.bea.wlw.netui.pageflow.internal.WeblogicPageFlowEventReporter.beginPageRequest(WeblogicPageFlowEventReporter.java:248)at org.apache.beehive.netui.pageflow.PageFlowPageFilter.doFilter(PageFlowPageFilter.java:154)at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:42)at com.bea.p13n.servlets.PortalServletFilter.doFilter(PortalServletFilter.java:336)at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:42)at weblogic.servlet.internal.RequestDispatcherImpl.invokeServlet(RequestDispatcherImpl.java:526)at weblogic.servlet.internal.RequestDispatcherImpl.forward(RequestDispatcherImpl.java:261)at <App>.AppRedirectFilter.doFilter(RedirectFilter.java:83)at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:42)at <App>.AppServletFilter.doFilter(PortalServletFilter.java:336)at weblogic.servlet.internal.FilterChainImpl.doFilter(FilterChainImpl.java:42)at weblogic.servlet.internal.WebAppServletContext$ServletInvocationAction.run(WebAppServletContext.java:3393)at weblogic.security.acl.internal.AuthenticatedSubject.doAs(AuthenticatedSubject.java:321)at weblogic.security.service.SecurityManager.runAs(Unknown Source)at weblogic.servlet.internal.WebAppServletContext.securedExecute(WebAppServletContext.java:2140)at weblogic.servlet.internal.WebAppServletContext.execute(WebAppServletContext.java:2046)at weblogic.servlet.internal.ServletRequestImpl.run(Unknown Source)at weblogic.work.ExecuteThread.execute(ExecuteThread.java:200)at weblogic.work.ExecuteThread.run(ExecuteThread.java:172)

如您所見,當試圖將調試信息記錄到配置的附加程序和日志文件中時,似乎所有線程都在等待獲取Apache Log4j對象監視器(org.apache.log4j.spi.RootCategory)上的鎖。 我們如何從此線程堆棧跟蹤中找出答案? 讓我們剖析此線程堆棧跟蹤,以便您更好地了解此線程競爭條件,例如250個試圖同時獲取同一對象監視器的線程。

此時的主要問題是為什么我們突然看到這個問題? 經過適當驗證后,此時還排除了日志記錄級別或負載的增加。 回滾以前的更改確實解決了問題,這一事實自然使我們對所推動的更改進行了更深入的審查。 在進入最終的根本原因部分之前,我們將對受影響的Log4j代碼進行代碼審查,例如,暴露于線程競爭條件下。

Apache Log4j 1.2.15代碼審查

## org.apache.log4j.Category
/*** Call the appenders in the hierrachy starting at <code>this</code>. If no* appenders could be found, emit a warning.** <p>* This method calls all the appenders inherited from the hierarchy* circumventing any evaluation of whether to log or not to log the* particular log request.** @param event*            the event to log.*/public void callAppenders(LoggingEvent event) {int writes = 0;for (Category c = this; c != null; c = c.parent) {// Protected against simultaneous call to addAppender,// removeAppender,...synchronized (c) {if (c.aai != null) {writes += c.aai.appendLoopOnAppenders(event);}if (!c.additive) {break;}}}if (writes == 0) {repository.emitNoAppenderWarning(this);}

如您所見,Catelogry.callAppenders()在類別級別使用了一個同步塊,這可能導致嚴重的并發負載下嚴重的線程競爭情況。 在這種情況下,使用重入式讀-寫鎖會更合適(例如,這種鎖策略允許并發的“讀”但允許單個“寫”)。 您可以在下面找到有關此已知Apache Log4j限制的參考以及一些可能的解決方案。

https://issues.apache.org/bugzilla/show_bug.cgi?id=41214

以上Log4j行為是否是我們問題的實際根本原因? 并沒有那么快……請記住,只有在最近部署之后才暴露此問題。 真正的問題是,哪些應用程序更改從Apache Log4j日志記錄API觸發了此問題和副作用?

根本原因:完美的風暴!

對最近部署的更改的深入研究確實表明,已刪除了子類加載器級別的某些Log4j庫以及相關的“子優先”策略。 這個重構練習最終將Commons日志記錄和Log4j的委派移到了類加載器級別。 問題是什么?

在進行此更改之前,日志記錄事件在父類加載器的Weblogic Beehive Log4j調用與子類加載器的Web應用程序日志記錄事件之間分配。 由于每個類加載器都有其自己的Log4j對象副本,因此線程競爭條件問題被分成兩半,并且在當前負載條件下不暴露(屏蔽)。 重構之后,所有Log4j調用都移到了父類加載器(Java EE應用程序)中。 為Log4j組件(例如Category)添加了顯著的并發級別。 并發級別的提高以及這種已知的Category.java線程爭用/死鎖行為對我們的生產環境是一場完美的風暴。

為了緩解此問題,還對環境應用了2種直接解決方案:

  • 在父類和子類加載器之間回滾重構和拆分的Log4j回調。
  • 將某些附加程序的日志記錄級別從DEBUG降低到WARNING

當應用更改(例如與庫和類加載器相關的更改)時,此問題案例再次增強了執行適當的測試和影響評估的重要性。 這樣的更改在“表面”上看起來很簡單,但可以觸發一些深層的執行模式更改,從而使您的應用程序處于已知的線程競爭條件下。

還將探索對Apache Log4j 2(或其他日志記錄API)的未來升級,因為它有望帶來一些性能增強,從而解決一些線程競爭和可伸縮性方面的問題。

請提供任何評論或分享您與日志API相關的線程競賽相關問題的經驗。

祝您編程愉快,別忘了分享!

參考: Log4j線程死鎖– Java EE支持模式和Java教程博客上的JCG合作伙伴 Pierre-Hugues Charbonneau的案例研究 。


翻譯自: https://www.javacodegeeks.com/2012/09/log4j-thread-deadlock-case-study.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/371360.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/371360.shtml
英文地址,請注明出處:http://en.pswp.cn/news/371360.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

代碼實現:求s=a+aa+aaa+aaaa+aa...a的值,其中a是一個數字

import java.util.Scanner;/*題目&#xff1a;求saaaaaaaaaaaa...a的值&#xff0c;其中a是一個數字。例如222222222222222(此時共有5個數相加)&#xff0c;幾個數相加有鍵盤控制。 程序分析&#xff1a;關鍵是計算出每一項的值。*/public class Test08 {public static void ma…

堆棧跟蹤從何而來?

我認為&#xff0c;閱讀和理解堆棧跟蹤是每個程序員都必須具備的一項基本技能&#xff0c;以便有效地解決每種JVM語言的問題&#xff08;另請參閱&#xff1a; 過濾日志中無關的堆棧跟蹤行和首先記錄引起異常的根本原因 &#xff09;。 那么我們可以從一個小測驗開始嗎&#xf…

@select 怎么寫存儲過程_MySQL4:存儲過程和函數

什么是存儲過程簡單說&#xff0c;存儲過程就是一條或多條SQL語句的集合&#xff0c;可視為批文件&#xff0c;但是起作用不僅限于批處理。本文主要講解如何創建存儲過程和存儲函數以及變量的使用&#xff0c;如何調用、查看、修改、刪除存儲過程和存儲函數等。使用的數據庫和表…

跨域訪問-預請求及跨域常見問題

預請求 參考&#xff1a;https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS#預請求 簡而言之&#xff0c;在跨域并且嘗試添加一些特殊頭及自定義頭的情況下&#xff0c;由于瀏覽器的安全機制&#xff0c;會加多一次OPTIONS預請求&#xff08;詢問請求&am…

mysql查詢優化之一:mysql查詢優化常用方式

一、為什么查詢速度會慢&#xff1f; 一個查詢的生命周期大致可以按照順序來看&#xff1a;從客戶端&#xff0c;到服務器&#xff0c;然后在服務器上進行解析&#xff0c;生成執行計劃&#xff0c;執行&#xff0c;并返回結果給客戶端。其中在“執行”階段包含了大量為了檢索…

修復Sonar中常見的Java安全代碼沖突

本文旨在向您展示如何快速修復最常見的Java安全代碼沖突。 它假定您熟悉代碼規則和違規的概念以及Sonar如何對其進行報告。 但是&#xff0c;如果您以前從未聽說過這些術語&#xff0c;則可以閱讀Sonar Concepts或即將出版的有關Sonar的書 &#xff0c;以獲取更詳細的解釋。 為…

理解ThreadLocal

ThreadLocal:線程本地存儲&#xff0c;為每個線程都創建了變量的副本&#xff0c;線程在訪問變量時&#xff0c;可以直接訪問自己內部的副本變量。 理解幾個概念&#xff1a; 在java中ThreadLocal是一個類。 ThreadMap是一個類&#xff0c; Thread類是線程類。 ThreadLocal…

抖音右上角一個小黃點是什么_抖音官方入駐視頻號,釋放了一個什么樣的信號?...

專注視頻號觀察第 328 篇這幾天&#xff0c;視頻號生態新入駐了一個企業號&#xff0c;在圈里引起不少的轟動&#xff0c;因為這個號的名字叫做———抖音。這件事在圈里引發不少的轟動&#xff0c;很多人驚嘆&#xff1a;“連抖音都來開視頻號了&#xff0c;你還在等什么&…

資源包技巧和最佳實踐

今天是資源捆綁日。 通常&#xff0c;這是Java中最著名的國際化機制&#xff08;i18n&#xff09;。 使用它應該很容易。 但是&#xff0c;在弄臟手時會出現許多小問題。 如果您有相同的想法&#xff0c;則此文章適合您。 基本 java.util.ResourceBundle定義了用于訪問Java中翻…

springMvc-文件上傳

趕時間&#xff0c;又在寫垃圾博客&#xff0c;在心里給自己一耳巴 1.單文件上傳 2.多文件上傳 代碼&#xff1a; 頁面&#xff1a; <!DOCTYPE html><html><head><meta charset"UTF-8"><title>Insert title here</title></he…

c cuda 指定gpu_GPU并行編程:熟練使用CUDA C語言

【IT168 專稿】一個大任務通常可能被分解成許多可以一起處理的小任務&#xff0c;以便創建一個解決方案&#xff0c;這和粉刷房子的道理是一樣的&#xff0c;在粉刷之前&#xff0c;假設你需要買5公升油漆和5把刷子&#xff0c;你可以自己一個人干完采購和粉刷的活&#xff0c;…

js中使用0 “” null undefined {}需要注意

注意&#xff1a;在js中0為空&#xff08;false&#xff09; &#xff0c;代表空的還有“”&#xff0c;null &#xff0c;undefined&#xff1b; 如果做判斷if(&#xff01;上面的四種值)&#xff1b;返回均為false console.log(!null);// true console.log(!0);//true consol…

PhpStorm 10.0.3破解版下載

漢化破解版軟件下載&#xff1a; http://pan.baidu.com/s/1geNO24r 密碼: d5ci 這個漢化破解軟件解決了大綱視圖里空白的問題。 先安裝騰訊電腦管家&#xff0c;然后安裝這個軟件&#xff0c;安裝到最后提示有個文件有病毒已刪除&#xff0c;點確定后正常使用。轉載于:https://…

Jenkins:部署JEE工件

隨著持續集成和持續交付的出現 &#xff0c;我們的構建被分為不同的步驟&#xff0c;以創建部署管道。 這些步驟中的一些步驟可以是例如編譯和運行快速測試&#xff0c;運行慢速測試&#xff0c;運行自動驗收測試或發布應用程序等。 部署流程的最后一步意味著將我們的產品&…

seafile 部署_Seafile開啟webdav及讀寫性能測試

為什么要在seafile搞webdavSeafile 一直是一款可靠的文件同步web應用&#xff0c;經過個人測試&#xff0c;同一臺機器上&#xff0c;seafile在傳輸文件時的速度比nextcloud要快&#xff08;可能也與php的設置有關系&#xff09;&#xff0c;這是seafile的優勢。但是&#xff0…

Python--校園網爬蟲記

查成績&#xff0c;算分數&#xff0c;每年的綜合測評都是個固定的過程&#xff0c;作為軟件開發者&#xff0c;這些過程當然可以交給代碼去做&#xff0c;通過腳本進行網絡請求獲取數據&#xff0c;然后直接進行計算得到基礎分直接填表就好了&#xff0c;查成績再手動計算既容…

Spring–添加SpringMVC –第1部分

歡迎來到本教程的第四部分。 在這一部分中&#xff0c;我們將使用Spring MVC編寫控制器和視圖&#xff0c;并考慮我們的REST模型。 我們必須做的第一件事&#xff0c;就是根據目前的情況制作一個Web應用程序。 我們將web / WEB-INF文件夾添加到我們的項目根目錄。 在WEB-INF內創…

[Linux] 權限與指令間的關系

我們知道權限對于使用者帳號來說是非常重要的&#xff0c;因為他可以限制使用者能不能讀取/創建/刪除/修改文件或目錄&#xff01; 在這一章我們介紹了很多文件系統的管理指令&#xff0c;第五章則介紹了很多文件權限的意義。在這個小節當中&#xff0c; 我們就將這兩者結合起來…

access month函數用法_學會了這7個EXCEL日期函數技巧,老板再讓你加班,你找我!...

日期函數&#xff0c;常用年月日&#xff0c;時分秒&#xff0c;星期&#xff0c;季度&#xff0c;求差值等&#xff0c;學會以下幾個函數&#xff0c;老板再讓你加班&#xff0c;你找我&#xff01;1、記錄當前時間(不隨系統時間變化)NOW()函數與數據有效性結合&#xff0c;記…

css樣式表的選擇器與分類

css 樣式表的作用&#xff1a; 主要用于結構,樣式與行為,CSS主要的作用就是美化網頁的一個語言,它的特點: 1.結構與樣式分離的方式,便于后期維護與改版; 2.樣式定義精確到像素的級別; css樣式表的結構&#xff1a;CSS 稱為層疊樣式表 用于給網頁設置各種樣式 css樣式的語法由3部…