日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費收錄網(wǎng)站服務,提交前請做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

一、背景

歷史原因,當前有一個服務專門用于處理mq消息,mq使用的阿里云rocketmq,sdk版本1.2.6(2016年)。隨著業(yè)務的發(fā)展,該應用上的consumer越來越多,接近200+,導致該應用所在的ecs長時間load高,頻繁報警。


二、現(xiàn)象分析

該應用所在的ecs服務器load長期飆高(該ecs上只有一個服務),但cpu、io、內(nèi)存等資源利用率較低,系統(tǒng)負載參考下圖:

記一次CPU使用率低負載高的排查過程

 

ECS配置:4核8G
物理cpu個數(shù)=4
單個物理CPU中核(core)的個數(shù)=1
單核多處理器
復制代碼

在系統(tǒng)負荷方面,多核CPU與多CPU效果類似,考慮系統(tǒng)負荷的時候,把系統(tǒng)負荷除以總的核心數(shù),只要每個核心的負荷不超過1.0,就表明正常運行。 通常,n核cpu時,load<n,系統(tǒng)負載都屬于正常情況。

套用以上規(guī)則: 先觀察load_15m和load_5m,load基本保持在3-5之間,說明系統(tǒng)中長期負載保持在一個較高的量級。 再觀察load_1m可以看出,波動很大,并且很多時間段內(nèi)遠大于cpu核心數(shù)。 短期內(nèi)繁忙,中長期內(nèi)緊張,很可能是一個擁塞的開始。


三、原因定位

排查導致load高的原因

tips:系統(tǒng)load高,不代表cpu資源不足。Load高只是代表需要運行的隊列累計過多。但隊列中的任務實際可能是耗cpu的,也可能是耗i/0及其他因素的
復制代碼
記一次CPU使用率低負載高的排查過程

 

圖中l(wèi)oad_15,load_5,load_1均大于核心數(shù)4,超負荷運行

  • 用戶進程=8.6%
  • 內(nèi)核進程 =9.7%
  • 空閑=80%
  • I/O等待所占用的cpu時間百分比=0.3%
記一次CPU使用率低負載高的排查過程

 

通過上圖CPU、內(nèi)存、IO使用情況,發(fā)現(xiàn)三者都不高, CPU使用率低負載高,排除cpu資源不足導致load高的可能性。

再通過vmstat查看進程、內(nèi)存、I/O等系統(tǒng)整體運行狀態(tài),如下圖:

記一次CPU使用率低負載高的排查過程

 

從結果上看,io的block in和block out 并不頻繁,但是system的中斷數(shù)(in)、上下文切換(cs)特別頻繁,進程上下文切換次數(shù)較多的情況下,很容易導致CPU將大量的時間耗費在寄存器、內(nèi)核棧、以及虛擬內(nèi)存等資源的保存和恢復上,進而縮短了真正運行進程的時間造成load高。

CPU寄存器,是CPU內(nèi)置的容量小、但速度極快的內(nèi)存。程序計數(shù)器,則是用來存儲CPU正在執(zhí)行的指令的位置,或者即將執(zhí)行的下一條指令的位置。
他們都是CPU在運行任何任務前,必須依賴的環(huán)境,因此也被叫做CPU上下文。

CPU上下文切換,就是先把前一個任務的CPU上下文(也就是CPU寄存器和程序計數(shù)器)保存起來,然后加載新任務的上下文,到這些寄存器和程序計數(shù)器,
最后再跳轉到程序計數(shù)器所指的新位置,運行新任務。
復制代碼

排查大方向:頻繁的中斷以及線程切換(由于該臺ecs上只存在一個JAVA服務,主要排查該進程)

通過vmstate只能查看總的cpu上下文切換,可通過pidstat命令查看線程層面的上下文切換信息 pidstat -wt 1(下圖拉的是9s的數(shù)據(jù),總共36w次,平均每秒4w次)

記一次CPU使用率低負載高的排查過程

 

觀察上圖,很容易發(fā)現(xiàn)兩個現(xiàn)象:

  1. 第一個是java線程特別多
  2. 第二個是很有規(guī)律的出現(xiàn)每秒上下文切換100+次的線程。(具體原因后面分析)

確認一下這些java線程的來源,查看該應用進程下的線程數(shù) cat /proc/17207/status

記一次CPU使用率低負載高的排查過程

 

線程數(shù)9749(非高峰)

排查方向:

  • 線程數(shù)過多
  • 部分線程每秒上下文切換次數(shù)過高

先排查主要原因,即部分線程上下文切換次數(shù)過高 拉一下線上該進程的堆棧信息,然后找到切換次數(shù)達到100+/每秒的線程id,把線程id轉成16進制后在堆棧日志中檢索

記一次CPU使用率低負載高的排查過程

 


記一次CPU使用率低負載高的排查過程

 

從上圖可以看到進程狀態(tài)TIME_WAITING,問題代碼,
com.alibaba.ons.open.trace.core.dispatch.impl.AsyncArrayDispatcher,多查幾個其他上下文切換頻繁的線程,堆棧信息基本相同。

再排查線程數(shù)過多的問題,分析堆棧信息會發(fā)現(xiàn)存在大量ConsumeMessageThread線程(通信、監(jiān)聽、心跳等線程先忽略)

記一次CPU使用率低負載高的排查過程

 

通過線程名稱,在rocketmq源碼中搜索基本能定位到下面這部分代碼

記一次CPU使用率低負載高的排查過程

 

通過兩段代碼,基本可以定位到問題出現(xiàn)在mq consumer初始化以及啟動的流程,后續(xù)根據(jù)代碼進行分析。


四、代碼分析

  1. 線程數(shù)過多代碼層面排查,從上面代碼截圖可以看到,ConsumeMessageThread_由線程池進行管理,再看一下線程池的關鍵參數(shù),核心線程數(shù)this.defaultMQPushConsumer.getConsumeThreadMin()、最大線程數(shù)this.defaultMQPushConsumer.getConsumeThreadMax()、無界隊列LinkedBlockingQueue
ps:由于線程池隊列用的LinkedBlockingQueue無界隊列,LinkedBlockingQueue的容量默認大小是Integer.Max,在任務沒有填滿這個容量之前不會創(chuàng)建新的工作線程,因此最大線程數(shù)沒有任何作用。
復制代碼

再看一下message-consumer對核心線程數(shù)以及最大線程數(shù)的配置,發(fā)現(xiàn)代碼層面沒有特殊配置,因此使用系統(tǒng)默認值,即下圖

記一次CPU使用率低負載高的排查過程

 


記一次CPU使用率低負載高的排查過程

 

至此,大致可以定位到線程數(shù)過多的原因:

由于未指定消費線程數(shù)量(ConsumeThreadNums),采用系統(tǒng)默認核心線程數(shù)20,最大線程數(shù)64.每個consumer初始化的時候都會創(chuàng)建一個核心線程數(shù)等于20的線程池,即大概率每個consumer都會存在20個線程消費消息, 導致線程數(shù)飆升(20*consumer個數(shù)),但發(fā)現(xiàn)這些消費線程大部分都處于sleep/wait狀態(tài),對上下文切換影響不大。

線程上下文切換次數(shù)過高代碼層面排查: 在rocketmq源碼中無法搜索到該段代碼,該應用使用阿里云sdk,在sdk中檢索,查看上下文以及調(diào)用鏈路,會發(fā)現(xiàn)這段代碼屬于軌跡回傳模塊。

記一次CPU使用率低負載高的排查過程

 

結合代碼分析一下軌跡回傳模塊的流程(AsyncArrayDispatcher),總結如下:

記一次CPU使用率低負載高的排查過程

 

在sdk源碼中定位線程堆棧日志中的代碼,如下:

記一次CPU使用率低負載高的排查過程

 

從這段代碼以及堆棧信息可以看到問題出現(xiàn)在traceContextQueue.poll(5,TimeUnit.MILLISECONDS);其中traceContextQueue為有界阻塞隊列,poll時,如果隊列為空,會阻塞一定時間,因此會導致線程在running和time_wait之間進行頻繁切換。

至于為什么要用poll(5,TimeUnit.MILLISECONDS)而不是take(),個人認為可能是為了減少網(wǎng)絡io,5ms批量取一次丟到線程池批量上報,避免單個軌跡頻繁上報?

poll()方法會返回隊列的第一個元素,并刪除;如果隊列為空,則返回null,并不會阻塞當前線程;出隊的邏輯調(diào)用的是 dequeue()方法,此外,它還有一個重載的方法,poll(long timeout, TimeUnit unit),如果隊列為空,則會等待一段時間
復制代碼
記一次CPU使用率低負載高的排查過程

 

軌跡隊列traceContextQueue使用的是ArrayBlockingQueue,一個有界的阻塞隊列,內(nèi)部使用一個數(shù)組來存放元素,通過鎖來實現(xiàn)并發(fā)訪問的,也是按照 FIFO 的原則對元素進行排列。

記一次CPU使用率低負載高的排查過程

 

通過上面的代碼可以看到,其通過reentrantLock 來實現(xiàn)并發(fā)的控制,ReentrantLock 提供了公平鎖與非公平鎖的實現(xiàn),但ArrayBlockingQueue默認情況下,使用的非公平鎖,不保證線程線程公平的訪問隊列。

所謂的公平是指阻塞的線程,按照阻塞的先后順序訪問隊列,非公平是指當隊列可用的時候,阻塞的線程都可以有爭奪線程訪問的資格,有可能先阻塞的線程最后才能訪問隊列。
復制代碼

由于每個consumer都只開了一個軌跡分發(fā)線程,所以這部分不存在競爭。

再看一下ArrayBlockingQueue的阻塞實現(xiàn)原理

記一次CPU使用率低負載高的排查過程

 


記一次CPU使用率低負載高的排查過程

 


記一次CPU使用率低負載高的排查過程

 

通過上面這部分代碼可以看到阻塞最終是通過park方法實現(xiàn),unsafe.park是個native方法

park這個方法會阻塞當前線程,當以下4種情況中的一種發(fā)生時,該方法才會返回

  • 與park對應的unpark執(zhí)行或已經(jīng)執(zhí)行時。
  • 線程被中斷時
  • 等待完time參數(shù)指定的毫秒數(shù)時
  • 異常現(xiàn)象發(fā)生

至此,系統(tǒng)線程切換以及中斷頻繁原因總結如下:

阿里云sdk中軌跡回發(fā)模塊,一個consumer有一個分發(fā)線程和一個軌跡隊列以及一個軌跡數(shù)據(jù)回發(fā)線程池,分發(fā)線程從軌跡隊列中取,取不到則阻塞5ms,取到塞到軌跡數(shù)據(jù)回發(fā)線程池,然后數(shù)據(jù)上報。過多的分發(fā)線程頻繁在running和time_wait狀態(tài)進行切換,導致系統(tǒng)load高。由于代碼層面未設置每個consumer消息消費的最大最小線程數(shù),導致每個consumer都會開20個核心線程進程消息消費,導致線程數(shù)量過多消耗系統(tǒng)資源以及空跑。


五、優(yōu)化方案

結合以上原因,進行針對性優(yōu)化

  • 代碼層面針對每個consumer設置線程數(shù)配置項,consumer可根據(jù)承載的業(yè)務等實際情況設置核心線程數(shù),減少整體的線程數(shù)目,避免大量線程空跑。
  • 以上分析用的是阿里云ons 1.2.6的版本,當前已經(jīng)迭代到了1.8.5版本,通過分析1.8.5版本的軌跡回傳模塊的源碼,發(fā)現(xiàn)對軌跡回傳增加了開關,配置軌跡回傳使用單個線程(單例),即全部consumer使用一個分發(fā)線程、一個軌跡有界隊列、一個軌跡上報線程池來處理,可以考慮驗證通過后升版本。
記一次CPU使用率低負載高的排查過程

 


記一次CPU使用率低負載高的排查過程

 


記一次CPU使用率低負載高的排查過程

 

分享到:
標簽:負載
用戶無頭像

網(wǎng)友整理

注冊時間:

網(wǎng)站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨大挑戰(zhàn)2018-06-03

數(shù)獨一種數(shù)學游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數(shù)有氧達人2018-06-03

記錄運動步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定