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

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

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

一、背景

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


二、現象分析

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

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

 

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

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

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


三、原因定位

排查導致load高的原因

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

 

圖中load_15,load_5,load_1均大于核心數4,超負荷運行

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

 

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

再通過vmstat查看進程、內存、I/O等系統整體運行狀態,如下圖:

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

 

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

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

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

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

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

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

 

觀察上圖,很容易發現兩個現象:

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

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

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

 

線程數9749(非高峰)

排查方向:

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

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

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

 


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

 

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

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

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

 

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

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

 

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


四、代碼分析

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

再看一下message-consumer對核心線程數以及最大線程數的配置,發現代碼層面沒有特殊配置,因此使用系統默認值,即下圖

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

 


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

 

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

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

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

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

 

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

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

 

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

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

 

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

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

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

 

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

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

 

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

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

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

再看一下ArrayBlockingQueue的阻塞實現原理

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

 


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

 


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

 

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

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

  • 與park對應的unpark執行或已經執行時。
  • 線程被中斷時
  • 等待完time參數指定的毫秒數時
  • 異?,F象發生

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

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


五、優化方案

結合以上原因,進行針對性優化

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

 


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

 


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

 

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

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

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

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

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

運動步數有氧達人2018-06-03

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

每日養生app2018-06-03

每日養生,天天健康

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

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