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