Logback與Log4j2日志框架性能對比與調(diào)優(yōu)方式
前言
看到目前線上大多日志框架測評大多從宏觀角度,直接對比異步同步的吞吐量,但是沒有考量到更深層的淘汰機制、等待策略、隊列長度等對性能表現(xiàn)的影響,因此本文將從更多的角度對比及分析兩款日志框架的性能表現(xiàn),通過JProfiler+Jmeter壓測及數(shù)據(jù)采集,從線程占用、鎖占用、宏觀耗時等多維度可視化數(shù)據(jù)。
性能測試
logback
同步日志
耗時


未經(jīng)過任何調(diào)優(yōu),采用Logback默認配置得出上圖,一百萬條日志打印耗時(ms),如圖:單線程下性能最佳,耗時隨線程數(shù)增加而下降。
線程占用
單線程

無阻塞狀態(tài)
多線程
多線程打印日志時,會產(chǎn)生大量線程阻塞,線程越多阻塞狀態(tài)越多
四線程

八線程

十六線程

鎖占用

線程發(fā)生多次占用鎖的情況。查看Logback源碼可得知,檢查容量、放入隊列、取出隊列都需要在取得鎖后進行
異步日志(隊列擴容)
樣本數(shù)100萬,隊列長度110萬
耗時


線程占用
單線程

多線程
四線程

八線程

十六線程

鎖占用

每次寫入隊列都需要占用鎖,同時Appender從隊列取出也需要占用鎖
異步日志(半隊列擴容)
樣本數(shù)100萬,隊列長度50萬,不啟用拋棄策略
耗時


線程占用
單線程

多線程
寫入耗時明顯增長,寫入過程仍然發(fā)生阻塞狀態(tài)
四線程

八線程

十六線程

鎖占用

log4j2
同步日志
樣本數(shù)100萬,Logger到Appender串行執(zhí)行,輸出到文件
耗時


線程占用
線程產(chǎn)生長時間的等待,主要是緩沖環(huán)溢出后無法寫入,生產(chǎn)者根據(jù)等待策略進入等待狀態(tài)
單線程

單線程生產(chǎn)不需要爭搶鎖,因此全程無阻塞
多線程
整體來看,阻塞的時間隨著線程增多而增多,因此多線程對同步日志影響極大,性能損失嚴重
四線程

八線程

十六線程

后續(xù)監(jiān)控因阻塞時間太長跳過
鎖占用

阻塞在Appender上的輸出流上,輸出流是在單線程中執(zhí)行的
異步日志(隊列擴容)
樣本數(shù)100萬,隊列長度110萬,使用Yield等待策略
耗時


單線程占用最高,耗時隨線程數(shù)增加而縮短,直到線程數(shù)超過CPU核數(shù)。單線程耗時與logback相當,多線程耗時比logback縮短了2倍
線程占用
單線程與多線程使用都無阻塞狀態(tài),保證足夠的隊列容量,能使日志操作保持高吞吐和低延遲,避免阻塞等待
單線程

多線程
四線程

八線程

使用與宿主機CPU核數(shù)相等的線程數(shù),日志寫入過程無阻塞、無線程切換
十六線程

異步日志(日志淘汰策略)
樣本數(shù)100萬,隊列長度50萬,啟用拋棄策略
耗時


線程占用
隊列長度50萬,正常來說應與半隊列擴容一樣,產(chǎn)生阻塞現(xiàn)象,但啟用了日志淘汰策略,無法寫入隊列的將直接拋棄不阻塞等待
單線程

多線程
四線程

八線程

十六線程

異步日志(半隊列擴容)
樣本數(shù)100萬,隊列長度50萬,使用Yield等待策略
耗時


當隊列滿后,大幅影響了響應時間,吞吐量依賴Appender的消費性能
線程占用
單線程記錄日志時,前半段隊列未滿時生產(chǎn)線程一直處于工作狀態(tài),后半段因消費能力跟不上生產(chǎn)能力,導致隊列滿載,生產(chǎn)線程開始出現(xiàn)等待狀態(tài)
單線程

等待的時間比多線程少,是因為單線程下日志生產(chǎn)速度慢,同時日志也在倍消費
多線程
前一段時間可以維持高性能工作,但后面隊列滿后開始發(fā)送等待,導致耗時延長
四線程

八線程

十六線程

鎖占用

并未發(fā)現(xiàn)日志記錄過程中發(fā)生鎖占用
異步日志(等待策略)
樣本數(shù)100萬,隊列長度50萬,使用Timeout等待策略
耗時


線程占用

未產(chǎn)生阻塞狀態(tài)
單線程
多線程
因Timeout等待策略使用了鎖,因此產(chǎn)生一定的阻塞
四線程

八線程

十六線程

鎖占用

使用Timeout等待策略時,放入隊列前會取鎖,進行消費者線程喚醒動作
性能調(diào)優(yōu)
異步日志
無論是logback還是log4j2,使用異步日志可以大幅提高日志操作耗時,間接提高業(yè)務方整體耗時
日志可靠性
異步日志無法保證日志可靠性,系統(tǒng)意外關(guān)閉會丟失隊列中的日志,因此要求高可靠的日志,應該選擇數(shù)據(jù)庫或者MQ來保證
Logback
通過
<appender name="async-log-all" class="ch.qos.logback.classic.AsyncAppender">
設置
Log4j2
通過
System.setProperty("Log4jContextSelector", "org.apache.logging.log4j.core.async.AsyncLoggerContextSelector")
設置
日志拋棄策略
將溢出隊列的日志拋棄,保持穩(wěn)定的響應速度。對業(yè)務方來說能保持良好、穩(wěn)定的日志服務,但需要容忍一定的日志丟
Log4j2
通過
System.setProperty("log4j2.AsyncQueueFullPolicy","Discard")
設置
Logback
通過<discardingThreshold>指定拋棄日志的閾值
拋棄邊界
當隊列剩余容量小于閾值后,將拋棄ERROR以下的日志
禁用拋棄策略
設置為0則表示不拋棄,業(yè)務線程等待隊列空間可用后寫入
默認閾值
默認閾值為隊列長度的20%,隊列長度100閾值為20
日志等待策略
Log4j2獨有的特性,指定隊列滿時,生產(chǎn)者進行等待的行為,需要在不開啟拋棄策略下進行
TimeoutWaitStrategy
Log4j2默認的等待策略,通過Object.wait等待隊列騰空。在放入隊列時會加鎖,不推薦使用。
YieldWaitStrategy
通過
System.setProperty("log4j2.asyncLoggerWaitStrategy","Yield")
設置。通過System.yield()等待隊列騰空,比Timeout等待策略更高效,比Busy等待策略更節(jié)能
隊列容量
由性能測試可知,不適用日志拋棄策略下,隊列滿載后生產(chǎn)線程將阻塞等待隊列騰空,直接影響業(yè)務方的效率
Logback
通過<queueSize>指定隊列長度,Logback固定使用ArrayBlockingQueue作為隊列
Log4j2
通過
System.setProperty("log4j2.asyncLoggerRingBufferSize","x")
指定
二次方長度
RingBuffer內(nèi)部計算位置時通過二進制方式計算,使用二的指數(shù)長度可以提高計算速度
長度計算公式
暫未找到統(tǒng)一標準的計算公式,本人覺得可以通過(日志峰值TPS#消費TPS)*15*60來計算
承載容量
這個公式的含義是:應用15分鐘以峰值去生產(chǎn)的日志可以全部被隊列容納
成本
從成本的角度看,隊列不應該無限量地預估,在保證系統(tǒng)不受到容量影響下,盡可能地使用小的長度,節(jié)省內(nèi)存開支
響應時間
一般應用不應該長時間在峰值運行,如果出現(xiàn)長時間在峰值運行,則應該進行水平拓展分散請求壓力。因此容納15分鐘之內(nèi)的峰值,可以有足夠時間讓運維響應,進行水平拓展分散壓力。
消費瓶頸
日志消費TPS由Appender消費效率決定,當日志TPS超過消費TPS時,日志將開始在隊列中堆積
消費TPS
某個Appender在一秒內(nèi)消費的日志數(shù)量,舉FileAppender為例,每條日志消費花費100微妙(性能好的主機可以到60),一秒可以消費1萬條日志,即消費TPS為1萬
請求TPS
一般Web應用不會承載超過消費TPS的流量。假設每個請求打印五條日志,則需要5000以上的TPS才能產(chǎn)生日志堆積
消費者優(yōu)化
日志TPS長時間(15min+)超過消費TPS的場景下,應該對消費能力進行優(yōu)化,使用輕量級的Appedner、簡單的Layout、日志拋棄策略、過濾器、業(yè)務方規(guī)范等方面進行優(yōu)化
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Assert.assertEquals的使用方法及注意事項說明
這篇文章主要介紹了Assert.assertEquals的使用方法及注意事項說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-05-05
SpringBoot實現(xiàn)滑塊驗證碼驗證登陸校驗功能詳解
驗證碼作為一種自然人的機器人的判別工具,被廣泛的用于各種防止程序做自動化的場景中。傳統(tǒng)的字符型驗證安全性已經(jīng)名存實亡的情況下,各種新型的驗證碼如雨后春筍般涌現(xiàn),今天給大家分享一篇SpringBoot實現(xiàn)滑塊驗證碼2022-09-09
Java操作數(shù)據(jù)庫(行級鎖,for update)
這篇文章主要介紹了Java操作數(shù)據(jù)庫(行級鎖,for update),文章圍繞Java操作數(shù)據(jù)庫的相關(guān)資料展開詳細內(nèi)容,需要的小伙伴可以參考一下,希望對你有所幫助2021-12-12
application.yml的格式寫法和pom.xml讀取配置插件方式
這篇文章主要介紹了application.yml的格式寫法和pom.xml讀取配置插件方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07

