JVM-排查問題

by shihang.mai

rocketMq監(jiān)控界面TPS上不去

Cloud-nms-dlq模塊
rocketMq監(jiān)控界面顯示Tps很低买猖,只有幾十

瘋狂GC->內(nèi)存增大到8G改橘,垃圾回收器改為G1,從TPS0->幾百

  1. 用arthas

    thread -n all
    

    發(fā)現(xiàn)很多業(yè)務(wù)線程在waiting狀態(tài),再用

    thread 線程號
    

    查看某個(gè)waiting線程的堆棧,發(fā)現(xiàn)在wait另外一個(gè)線程,然后拿到線程的16進(jìn)制數(shù)轉(zhuǎn)換10執(zhí)行后

    thread 線程號
    

    在線程棧中找到究竟waiting什么鬼玉控,最終發(fā)現(xiàn)是關(guān)于logback日志的,CachingDateFormatter類加了Sync

    JVM優(yōu)化CachingDateFormatter.png
  2. 同時(shí)引入了JMC

    啟動(dòng)時(shí)加入jvm參數(shù)

    -Djava.net.preferIPv4Stack=true
    -XX:+FlightRecorder
    -XX:FlightRecorderOptions=stackdepth=128
    -XX:+UnlockDiagnosticVMOptions
    -XX:+DebugNonSafepoints
    

    然后命令開始搜集飞主、結(jié)束

    jcmd 9955 JFR.start name=MyRecording settings=profile
    
    jcmd 9955 JFR.dump name=MyRecording filename=/home/yundiao/1736.jfr
    
    jcmd 9955 JFR.stop name=MyRecording
    

    然后將jfr文件用飛行器分析,結(jié)果和arthas分析一樣

  3. 就是多線程寫日志文件,導(dǎo)致IO競爭(原本直接在yml直接設(shè)置日志)

    • 異步日志碌识。但會(huì)有溢出碾篡,重啟時(shí)會(huì)丟失日志
    • 按線程寫文件
  4. 選擇按線程寫文件解決IO競爭(改為引入logback.xml配置)

  5. logback有一個(gè)叫SiftingAppender的類,可以做按線程寫日志筏餐,只需注入自定義Discriminator开泽,日志名分發(fā)器

    public class ThreadDiscriminator extends ContextBasedDiscriminator {
        String KEY ="threadName";
        
        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;
        }
    }
    

    日志配置增加appender

    <appender name="frameworkthread" class="ch.qos.logback.classic.sift.SiftingAppender">
            <discriminator class="com.iwhalecloud.dlq.commonbean.ThreadDiscriminator">
                <key>threadName</key>
            </discriminator>
            <sift>
                <appender name="FILE-${threadName}" class="ch.qos.logback.core.rolling.RollingFileAppender">
                    <encoder>
                        <Encoding>UTF-8</Encoding>
                        <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS}[%c][%thread][%X{tradeNo}][%p]-%m%n</pattern>
                    </encoder>
                    <rollingPolicy
                            class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
                        <fileNamePattern>${log.path}/%d{yyyy-MM-dd,aux}/${threadName}-%d{yyyy-MM-dd}.%i.log
                        </fileNamePattern>
                        <maxFileSize>100MB</maxFileSize>
                        <maxHistory>3</maxHistory>
                    </rollingPolicy>
                </appender>
            </sift>
        </appender>
    
  6. 確實(shí)可以按線程寫日志,然后再看RocketMq的TPS魁瞪,竟然性能沒所提升穆律,還會(huì)下降。

    • 然后再用JMC分析發(fā)現(xiàn)這次在AppenderBase.doAppender()导俘,然后查看源碼峦耘,發(fā)現(xiàn)這個(gè)方法是sync的,而這個(gè)方法是我上面的SiftingAppender入口

      JMC分析結(jié)果
    • 分析原因旅薄,在我獲取自己的SiftingAppender時(shí)辅髓,要經(jīng)過父類的這個(gè)方法,所以肯定更低Tps

  7. 然后看接口Appender的實(shí)現(xiàn)類有兩個(gè)基礎(chǔ)的抽象

  • 非同步:UnsynchronizedAppenderBase,用了ThreadLocal

    • 同步:AppenderBase,用sync

    增加類UnsynchronizedSiftingAppenderBase extends UnsynchronizedAppenderBase

    創(chuàng)建類ParrelSiftingAppender extends UnsynchronizedSiftingAppenderBase

    修改SiftAction類,增加我的ParrelSiftingAppender的注冊分支

    public class SiftAction extends Action implements InPlayListener {
        List<SaxEvent> seList;
    
        public SiftAction() {
        }
    
        public void begin(InterpretationContext ic, String name, Attributes attributes) throws ActionException {
            this.seList = new ArrayList();
            ic.addInPlayListener(this);
        }
    
        public void end(InterpretationContext ic, String name) throws ActionException {
            ic.removeInPlayListener(this);
            Object o = ic.peekObject();
            if (o instanceof SiftingAppender) {
                SiftingAppender sa = (SiftingAppender)o;
                Map<String, String> propertyMap = ic.getCopyOfPropertyMap();
                AppenderFactoryUsingJoran appenderFactory = new AppenderFactoryUsingJoran(this.seList, sa.getDiscriminatorKey(), propertyMap);
                sa.setAppenderFactory(appenderFactory);
            }
              //增加判斷分支
    
        }
    
        public void inPlay(SaxEvent event) {
            this.seList.add(event);
        }
    
        public List<SaxEvent> getSeList() {
            return this.seList;
        }
    }
    
  1. 看TPS 提高了大概5倍左右少梁,效果還行利朵,再用jmc分析,然后我崩潰了猎莲,竟然還有绍弟,出現(xiàn)在類UnsynchronizedSiftingAppenderBase

    protected void append(E event) {
            if (this.isStarted()) {
                String discriminatingValue = this.discriminator.getDiscriminatingValue(event);
                long timestamp = this.getTimestamp(event);
              //競爭出現(xiàn)在這
                Appender<E> appender = (Appender)this.appenderTracker.getOrCreate(discriminatingValue, timestamp);
                if (this.eventMarksEndOfLife(event)) {
                    this.appenderTracker.endOfLife(discriminatingValue);
                }
    
                this.appenderTracker.removeStaleComponents(timestamp);
                appender.doAppend(event);
            }
        }
    
  2. 這不能忍,看一下源碼著洼,發(fā)現(xiàn)synchronized樟遣,getOrCreate是同步的。點(diǎn)進(jìn)去看LinkedHashMap做lru身笤,怪不得要加synchronized

  3. 然后開始修改appenderTracker

    • 增加AbstractConcurrentMapComponentTracker,參考AbstractComponentTracker
    • 增加ConcurrentMapAppenderTracker繼承AbstractConcurrentMapComponentTracker
    • 修改UnsynchronizedSiftingAppenderBase使用ConcurrentMapAppenderTracker進(jìn)行Appender管理
  4. 最終提高了10倍豹悬。終于結(jié)束

  5. 總結(jié)

    JVM調(diào)休logback多線程寫日志
    1. TPS=0,內(nèi)存2g->8g,gc回收器改為G1液荸,TPS=0 -> TPS=幾百
    2. 用arthas和JMC分析出是因?yàn)槎嗑€程寫日志導(dǎo)致TPS低
    3. 首先直接用SiftingAppender進(jìn)行分線程寫日志瞻佛,確實(shí)可以做到分線程寫日志,但是觀察TPS比之前更低
      • 原因:進(jìn)入SiftingAppender前需要進(jìn)入父類AppenderBase.doAppender(),這個(gè)是sync的娇钱,同步向上提高了伤柄,所以TPS比以前低很正常
    4. 查看Appender接口,發(fā)現(xiàn)有實(shí)現(xiàn)類UnSyncAppenderBase,所以參考SiftingAppender實(shí)現(xiàn)
      • ?類UnSyncSiftingAppenderBase extends UnSyncAppenderBase
      • ?類ParrelSiftingAppender extends UnSyncSiftingAppenderBase
      • 修改SiftAction,注冊上我寫的ParrelSiftingAppender
    5. 此時(shí)發(fā)現(xiàn)TPS提高了5倍左右文搂,再用JMC分析适刀,發(fā)現(xiàn)還有IO競爭
      • 原因:UnSyncSiftingAppenderBase類會(huì)調(diào)用AbstractComponentTracker的sync方法getOrCreate()
      • 因?yàn)锳bstractComponentTracker用LinkedHashMap做LRU,LinkedHashMap非線程安全,?sync正常
    6. 用ConcurrentMap替代LinkedHashMap
      • ?類AbstractConcurrentMapComponentTracker
      • ?類ConcurrentMapAppenderTracker extends AbstractConcurrentMapComponentTracker
      • 修改UnSyncSiftingAppenderBase類調(diào)用AbstractConcurrentMapComponentTracker的getOrCreate()
    7. TPS提高了10倍煤蹭。
      • 原本業(yè)務(wù)中用到日志的地方都要修改笔喉,?MDC.put("threadName", xxx)取视。
      • 自定義類ThreadDiscriminator繼承ContextBasedDiscriminator就可以免去修改業(yè)務(wù)的所有日志的地方
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市常挚,隨后出現(xiàn)的幾起案子作谭,更是在濱河造成了極大的恐慌,老刑警劉巖奄毡,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件折欠,死亡現(xiàn)場離奇詭異,居然都是意外死亡秧倾,警方通過查閱死者的電腦和手機(jī)怨酝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來那先,“玉大人农猬,你說我怎么就攤上這事∈鄣” “怎么了斤葱?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長揖闸。 經(jīng)常有香客問我揍堕,道長,這世上最難降的妖魔是什么汤纸? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任衩茸,我火速辦了婚禮,結(jié)果婚禮上贮泞,老公的妹妹穿的比我還像新娘楞慈。我一直安慰自己,他們只是感情好啃擦,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布囊蓝。 她就那樣靜靜地躺著,像睡著了一般令蛉。 火紅的嫁衣襯著肌膚如雪聚霜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天珠叔,我揣著相機(jī)與錄音蝎宇,去河邊找鬼。 笑死运杭,一個(gè)胖子當(dāng)著我的面吹牛夫啊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播辆憔,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼撇眯,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了虱咧?” 一聲冷哼從身側(cè)響起熊榛,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎腕巡,沒想到半個(gè)月后玄坦,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡绘沉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年煎楣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片车伞。...
    茶點(diǎn)故事閱讀 39,981評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡择懂,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出另玖,到底是詐尸還是另有隱情困曙,我是刑警寧澤,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布谦去,位于F島的核電站慷丽,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏鳄哭。R本人自食惡果不足惜要糊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望妆丘。 院中可真熱鬧锄俄,春花似錦、人聲如沸飘痛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽宣脉。三九已至车柠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間塑猖,已是汗流浹背竹祷。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留羊苟,地道東北人塑陵。 一個(gè)月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像蜡励,于是被迫代替她去往敵國和親令花。 傳聞我的和親對象是個(gè)殘疾皇子阻桅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評論 2 355

推薦閱讀更多精彩內(nèi)容