?AsyncLoggerConfig 導(dǎo)致線程 Block
通過(guò)監(jiān)控平臺(tái)查看線程監(jiān)控指標(biāo),從 Blocked 線程堆棧不難看出是和日志打印相關(guān)。分析異常線程堆棧 與(AsyncAppender 導(dǎo)致線程 Block)業(yè)務(wù)異常一樣划鸽。
解析異常堆棧
Log4j2 關(guān)于日志的幾個(gè)重要概念:
● ,日志配置標(biāo)簽靡狞,用于 XML 日志配置文件中褪那,對(duì)應(yīng) Log4j2 框架中的 LoggerConfig 類(lèi),同步分發(fā)日志事件到對(duì)應(yīng) Appender疚俱。
● 劝术,日志配置標(biāo)簽,用于 XML 日志配置文件中呆奕,對(duì)應(yīng) Log4j2框架中的?AsyncLoggerConfig?類(lèi)养晋,內(nèi)部使用 Disruptor 隊(duì)列異步分發(fā)日志事件到對(duì)應(yīng) Appender。
● Logger梁钾,同步日志類(lèi)绳泉,用于創(chuàng)建同步日志實(shí)例,同步調(diào)用 ReliabilityStrategy處理日志姆泻。
● AsyncLogger**零酪,異步日志類(lèi),用于創(chuàng)建異步日志實(shí)例**拇勃,內(nèi)部使用?Disruptor 隊(duì)列實(shí)現(xiàn)異步調(diào)用 ReliabilityStrategy 處理日志
? ? ? ?由于未配置 Log4jContextSelector 參數(shù)四苇,所以使用的是同步 Logger,即通過(guò) LoggerFactory.getLogger 方法獲取的是?Logger 類(lèi)實(shí)例而不是?AsyncLogger類(lèi)實(shí)例方咆,同時(shí)由于項(xiàng)目的 log4j2.xml 配置文件里配置了 標(biāo)簽月腋,所以其底層是 Logger 和 AsyncLoggerConfig 組合。
? ? ? ? ? AsyncLoggerConfig 處理日志事件,其內(nèi)部使用 Disruptor隊(duì)列罗售,在生成隊(duì)列元素時(shí)辜窑,由 translator 來(lái)負(fù)責(zé)填充元素字段,并把填充后的元素放入 RingBuffer 中寨躁,于此同時(shí)穆碎,獨(dú)立的異步線程從 RingBuffer 中消費(fèi)事件,并調(diào)用配置在該 AsyncLoggerConfig 上的 Appender 處理日志請(qǐng)求职恳。
?AsyncLoggerConfig 提 供 了 帶 有 Disruptor 隊(duì) 列 實(shí) 現(xiàn) 的 代 理 類(lèi)?即 AsyncLoggerConfigDisruptor所禀, 在 日 志 事 件 進(jìn) 入 RingBuffer 時(shí), 由 于 項(xiàng) 目 使 用 的 是ReusableLogEventFactory放钦,所以由 MUTABLE_TRANSLATOR 負(fù)責(zé)初始化日志事件色徘,在此過(guò)程中會(huì)調(diào)用 getThrownProxy 方法創(chuàng)建 ThrowableProxy 實(shí)例,進(jìn)而在?ThrowableProxy 構(gòu)造函數(shù)內(nèi)部觸發(fā)解析操禀、加載異常堆棧類(lèi)褂策。
問(wèn)題小結(jié)
?Log4j2 打印異常日志時(shí),AsyncLoggerConfig 會(huì)初始化 Disruptor RingBuffer 日志元素字段颓屑,并進(jìn)一步觸發(fā)解析斤寂、加載異常堆棧類(lèi)。JVM 通過(guò)生成字節(jié)碼的方式優(yōu)化反射調(diào)用性能揪惦,但該動(dòng)態(tài)生成的類(lèi)無(wú)法被 WebAppClassLoader 類(lèi)加載器加載遍搞,因此當(dāng)大量包含反射調(diào)用的異常堆棧被輸出到日志時(shí),會(huì)頻繁地觸發(fā)類(lèi)加載器腋,由于類(lèi)加載過(guò)程是?synchronized 同步加鎖的溪猿,且每次加載都需要讀取文件,速度較慢纫塌,從而導(dǎo)致線程 Block诊县。