溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Java日志性能實(shí)例分析

發(fā)布時(shí)間:2021-12-27 16:30:51 來源:億速云 閱讀:180 作者:iii 欄目:大數(shù)據(jù)

這篇文章主要介紹“Java日志性能實(shí)例分析”,在日常操作中,相信很多人在Java日志性能實(shí)例分析問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Java日志性能實(shí)例分析”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

說到Java日志,大家肯定都會說要選擇合理的日志級別、合理控制日志內(nèi)容,但是這僅是萬里長征第一步……哪怕一些DEBUG級別的日志在生產(chǎn)環(huán)境中不會輸出到文件中,也可能帶來不小的開銷。我們撇開判斷和方法調(diào)用的開銷,在Log4J 2.x的性能文檔中有這樣一組對比:

logger.debug("Entry number: " + i + " is " +  String.valueOf(entry[i])); 
 logger.debug("Entry number: {} is {}", i, entry[i]);

上面兩條語句在日志輸出上的效果是一樣的,但是在關(guān)閉DEBUG日志時(shí),它們的開銷就不一樣了,主要的影響在于字符串轉(zhuǎn)換和字符串拼接上,無論是否生效,前者都會將變量轉(zhuǎn)換為字符串并進(jìn)行拼接,而后者則只會在需要時(shí)執(zhí)行這些操作。Log4J官方的測試結(jié)論是兩者在性能上能相差兩個(gè)數(shù)量級。試想一下,如果某個(gè)對象的toString()方法里用了ToStringBuilder來反射輸出幾十個(gè)屬性時(shí),這時(shí)能省下多少資源。

因此,某些仍在使用Log4J 1.x或Apache Commons Logging(它們不支持{}模板的寫法)的公司都會有相應(yīng)的編碼規(guī)范,要求在一定級別的日志(比如DEBUGINFO)輸出前增加判斷:

if (logger.isDebugEnabled()) { 
    logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i])); 
}

除了日志級別和日志消息,通常在日志中還會包含一些其他信息,比如日期、線程名、類信息、MDC變量等等,根據(jù)Takipi的測試,如果在日志中加入class,性能會急劇下降,比起LogBack的默認(rèn)配置,吞吐量的降幅在6成左右。如果一定要打印類信息,可以考慮用類名來命名Logger。

在分布式系統(tǒng)中,一個(gè)請求可能會經(jīng)過多個(gè)不同的子系統(tǒng),這時(shí)最好生成一個(gè)UUID附在請求中,每個(gè)子系統(tǒng)在打印日志時(shí)都將該UUID放在MDC里,便于后續(xù)查詢相關(guān)的日志?!禩he Ultimate Guide: 5 Methods For Debugging Production Servers At Scale》一文中就如何在生產(chǎn)環(huán)境中進(jìn)行調(diào)試給出了不少建議,當(dāng)中好幾條是關(guān)于日志的,這就是其中之一。另一條建議是記錄下所有未被捕獲的日志,其實(shí)拋出異常有開銷,記錄異常同樣會帶來一定的開銷,主要原因是Throwable類的fillInStackTrace方法默認(rèn)是同步的:

public synchronized native Throwable fillInStackTrace();

一般使用logger.error都會打出異常的堆棧,如果對吞吐量有一定要求,在情況運(yùn)行時(shí)可以考慮覆蓋該方法,去掉synchronized native,直接返回實(shí)例本身。

聊完日志內(nèi)容,再來看看Appender。在Java中,說起IO操作大家都會想起NIO,到了JDK 7還有了AIO,至少都知道讀寫加個(gè)Buffer,日志也是如此,同步寫的Appender在高并發(fā)大流量的系統(tǒng)里多少有些力不從心,這時(shí)就該使用AsyncAppender了,同樣是使用LogBack:

在10線程并發(fā)下,輸出200字符的INFO日志,AsyncAppender的吞吐量最高能是FileAppender的3.7倍。在不丟失日志的情況下,同樣使用AsyncAppender,隊(duì)列長度對性能也會有一定影響。

如果使用Log4J 2.x,那么除了有AsyncAppender,還可以考慮性能更高的異步Logger,由于底層用了Disruptor,沒有鎖的開銷,性能更為驚人。根據(jù)Log4J 2.x的官方測試,同樣使用Log4J 2.x:

64線程下,異步Logger比異步Appender快12倍,比同步Logger快68倍。

同樣是異步,不同的庫之間也會有差異:

同等硬件環(huán)境下,Log4J 2.x全部使用異步Logger會比LogBack的AsyncAppender快12倍,比Log4J 1.x的異步Appender快19倍。

Java日志性能實(shí)例分析

Log4J 2.x的異步Logger性能強(qiáng)悍,但也有不同的聲音,覺得這只是個(gè)看上去很優(yōu)雅,只能當(dāng)成一個(gè)玩具。關(guān)于這個(gè)問題,還是留給讀者自己來思考吧。

如果一定要用同步的Appender,那么可以考慮使用ConsoleAppender,然后將STDOUT重定向到文件里,這樣大約也能有10%左右的性能提升。

大部分生產(chǎn)系統(tǒng)都是集群部署,對于分布在不同服務(wù)器上的日志,用Logstash之類的工具收集就好了。很多時(shí)候還會在單機(jī)上部署多實(shí)例以便充分利用服務(wù)器資源,這時(shí)千萬不要貪圖日志監(jiān)控或者日志查詢方便,將多個(gè)實(shí)例的日志寫到同一個(gè)日志文件中,雖然LogBack提供了prudent模式,能夠讓多個(gè)JVM往同一個(gè)文件里寫日志,但此種方式對性能同樣也有影響,大約會使性能降低10%。

如果對同一個(gè)日志文件有大量的寫需求,可以考慮拆分日志到不同的文件,做法之一是添加多個(gè)Appender,同時(shí)修改代碼,不同的情況使用不同Logger;LogBack提供了SiftingAppender,可以直接根據(jù)MDC的內(nèi)容拆分日志,Jetty的教程中就有根據(jù)host來拆分日志的范例,而根據(jù)Takipi的測試,SiftingAppender的性能會隨著拆分文件數(shù)的增長一同提升,當(dāng)拆分為4個(gè)文件時(shí),10并發(fā)下SiftingAppender的吞吐量約是FileAppender的3倍多。

到此,關(guān)于“Java日志性能實(shí)例分析”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI