摘要:由于不是線程安全的,故在方法上增加了同步操作,造成競爭等待。至此,整個多線程調(diào)優(yōu)結(jié)束,通過充分優(yōu)化同步競爭的方式,最終使得分線程記錄日志的性能比最原始的多線程寫同一文件提高了倍去鎖提高到倍,替換提高倍
背景
??在一次項目的性能調(diào)優(yōu)中,發(fā)現(xiàn)出現(xiàn)競爭瓶頸,導(dǎo)致在資源未使用滿的情況下,TPS已經(jīng)無法提升。祭起JMC(JAVA MISSON CONTROL)飛行記錄器大法后,發(fā)現(xiàn)線程集中等待在logback寫日志的地方,如下圖所示:
??由于項目組多線程寫如同一個文件日志,導(dǎo)致存在IO競爭,一般解決這種問題有三種選擇方式
:
異步日志,但是會存在斷電或者日志隊列溢出丟失的可能
遠(yuǎn)程日志,日志放入外部消息隊列,保證持久化,但需額外部署日志存儲隊列
多線程日志,按線程(或線程取模)記錄日志,減少競爭,日志也能保證持久化
??項目組權(quán)衡再三,決定采用第三種分線程日志的方式解決。
誤入SiftingAppender大坑??項目組使用logback作為日志組件,loback是否有自動分線程寫日志的功能呢?網(wǎng)上搜索logack multiThread 的第一篇文章就是教你如何使用SiftingAppender來分線程記錄日志如下:
https://dzone.com/articles/si...
??SiftingAppender是logback根據(jù)mdc中的變量動態(tài)創(chuàng)建appender的代理,只要我們將一個線程號作為日志名分發(fā)器discriminator注入到SiftingAppender中,它就可以動態(tài)的為我們創(chuàng)建不同的appender,達(dá)到分線程的目的,配置方式舉例如下:
配置discriminator
public class ThreadDiscriminator extends ContextBasedDiscriminator { String KEY ="threadName"; /** * Return the name of the current context name as found in the logging event. */ public String getDiscriminatingValue(ILoggingEvent event) { return event.getThreadName(); } public String getDefaultValue() { return KEY; } public String getKey() { return KEY; } public void setKey(String key) { this.KEY = key; } }
配置logback appender
threadName UTF-8 %d{yyyy-MM-dd HH:mm:ss.SSS}[%c][%thread][%X{tradeNo}][%p]-%m%n D:/test/threadlogs/${threadName}-%d{yyyy-MM-dd}.%i.log 100MB 60 20GB
??配置后查看輸出結(jié)果也完全正確,網(wǎng)上的方法非常靠譜,真是piece of cake!但是接下來的性能測試讓我再次懵逼:性能沒有任何提升!反而更加糟糕了!這是怎么一回事呢?繼續(xù)jmc查看線程狀態(tài):
??這次寫文件outputStream的IO等待竟然提升到了AppenderBase.doAppender方法級別!查看AppenderBase.doAppender實現(xiàn),這個方法是Synchronized!這個父類的方法是SiftingAppender的入口方法,這意味著在獲取/創(chuàng)建線程自己真正的Appender和寫入日志之前都必須排隊阻塞在這個方法上!阻塞的級別提升了,當(dāng)然性能更糟糕了!
??logback Appender有兩個最基礎(chǔ)的抽象類,一個是同步AppenderBase,一個是不同步的UnsynchronizedAppenderBase,這兩個類功能一樣,只是doAppender方法的同步策略不一樣(Synchronize VS ThreadLocal)。那么SiftingAppender為什么繼承了AppenderBase而不是UnsynchronizedAppenderBase呢?分析原因應(yīng)該是SiftingAppender作為Appender的代理集合,它即可能包含了繼承自UnsynchronizedAppenderBase的OutputStreamAppender(FileAppender的基類,自行實現(xiàn)底層同步,doAppend方法未同步),也可能包含了繼承AppenderBase的SocketAppender類(doAppend方法同步),為防止出現(xiàn)線程安全問題,它直接在自身的doAppend方法上進行了同步。
??在項目組實際使用時,項目組只需要分線程寫文件日志,這意味這它使用的最終FileAppender應(yīng)該是線程安全,完全獨立的。故我們嘗試在logback新增繼承于UnsynchronizedAppenderBase的ParrelSiftingAppender步驟如下:
logback core中增加繼承UnsynchronizedAppenderBase的UnsynchronizedSiftingAppenderBase
logback classic中增加繼承UnsynchronizedSiftingAppenderBase的ParrelSiftingAppender
logback classic中SiftAction注冊增加ParrelSiftingAppender的工廠注冊
??修改完成后測試發(fā)現(xiàn)果然性能提升了5倍左右,CPU資源利用率接近飽和,應(yīng)該基本達(dá)到效果了,JMC分析應(yīng)該是沒有競爭了把,但是發(fā)現(xiàn)新的競爭方法出現(xiàn)了:
Appender最終改進:使用ConcurrentHashMap替換LinkedHashMapappender = appenderTracker.getOrCreate(discriminatingValue, timestamp);
??原來在SiftingAppender內(nèi)部使用了LinkedHashMap作為LRU Cache來管理Appender,會定期移除。由于LinkedHashMap不是線程安全的,故在getOrCreate方法上增加了Synchronized同步操作,造成競爭等待。
??結(jié)合業(yè)務(wù)場景,這里完全可以使用ConcurrentHashMap作為Appender的緩存,ConcurrentHashMap讀寫鎖分離,并且key值分區(qū)上鎖,在多讀少寫的情況下,有很高的并發(fā)性能。并且真正生成的日志Appender也并不多(100個左右),定時清理完全也不會出現(xiàn)內(nèi)存溢出問題。
??開始動手修改appenderTracker步驟如下:
logback core中增加AbstractConcurrentMapComponentTracker
logback core中增加ConcurrentMapAppenderTracker繼承
AbstractConcurrentMapComponentTracker
修改UnsynchronizedSiftingAppenderBase使用ConcurrentMapAppenderTracker進行Appender管理
??最終性能測試結(jié)果:在資源相同的情況下,優(yōu)化后比使用LinkedHashMap性能提高一倍。至此,整個logback多線程調(diào)優(yōu)結(jié)束,通過充分優(yōu)化同步競爭的方式,最終使得分線程記錄日志的性能比最原始的多線程寫同一文件提高了10倍(SiftAppender去鎖提高到5倍,Map替換提高2倍)!
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/76914.html
摘要:做好的優(yōu)化能大大提升系統(tǒng)的性能體系結(jié)構(gòu)概覽大致流程如圖編譯好的文件通過類加載器從物理結(jié)構(gòu)轉(zhuǎn)換成運行時數(shù)據(jù)區(qū)結(jié)構(gòu)。后面再寫一篇關(guān)于調(diào)優(yōu)的 什么是jvm jvm是java虛擬機的縮寫。所有的java程序都是在jvm上運行的。做好jvm的優(yōu)化能大大提升系統(tǒng)的性能 jvm體系結(jié)構(gòu)概覽 showImg(https://segmentfault.com/img/bVba5lB?w=1049&h=6...
摘要:這就是我和大佬的差距嗎看看別人是怎么做性能調(diào)優(yōu)的性能調(diào)優(yōu)后來的幾年里,我又陸續(xù)參與過物流電商游戲支付系統(tǒng)的研發(fā),這些項目都存在一個共性,就是經(jīng)常會運營一些大促以及搶購類活動。 先給大家講個故事吧。多年前我加入了一家大型互聯(lián)網(wǎng)公司,剛進入就以 996 標(biāo)準(zhǔn),參與新品研發(fā)。公司業(yè)務(wù)發(fā)展急需互聯(lián)網(wǎng)產(chǎn)品,因此我們的時間很緊張,4 ...
摘要:指標(biāo)虛擬內(nèi)存已使用的大小,如果大于,表示你的機器物理內(nèi)存不足了每秒從磁盤讀入虛擬內(nèi)存的大小,如果這個值大于,表示物理內(nèi)存不夠用或者內(nèi)存泄露了,要查找耗內(nèi)存進程解決掉。每秒虛擬內(nèi)存寫入磁盤的大小,如果這個值大于,同上,單位為。 原理剖析(第 013 篇)應(yīng)用系統(tǒng)性能調(diào)優(yōu) - 一、大致介紹 1. 本人接手的一個打車系統(tǒng),因為出現(xiàn)了一次響應(yīng)十分緩慢的情況,因此才有了應(yīng)用調(diào)優(yōu)的篇章; 2、由于...
面試官:今天要不來聊聊JVM調(diào)優(yōu)相關(guān)的吧?面試官:你曾經(jīng)在生產(chǎn)環(huán)境下有過調(diào)優(yōu)JVM的經(jīng)歷嗎?候選者:沒有面試官:...候選者:嗯...是這樣的,我們一般優(yōu)化系統(tǒng)的思路是這樣的候選者:1. 一般來說關(guān)系型數(shù)據(jù)庫是先到瓶頸,首先排查是否為數(shù)據(jù)庫的問題候選者:(這個過程中就需要評估自己建的索引是否合理、是否需要引入分布式緩存、是否需要分庫分表等等)候選者:2. 然后,我們會考慮是否需要擴容(橫向和縱向都...
閱讀 1285·2023-04-25 19:10
閱讀 1159·2021-09-10 10:50
閱讀 3042·2021-09-02 15:21
閱讀 1401·2019-08-30 15:52
閱讀 1697·2019-08-30 13:56
閱讀 2099·2019-08-30 12:53
閱讀 1886·2019-08-28 18:22
閱讀 2136·2019-08-26 13:47