<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          線程池如何監(jiān)控,才能幫助開發(fā)者快速定位線上錯(cuò)誤?

          共 5025字,需瀏覽 11分鐘

           ·

          2021-12-26 21:53

          大部分情況下,線程池的運(yùn)行情況對(duì)于使用者來說是個(gè)黑盒

          運(yùn)行情況不可知,會(huì)導(dǎo)致 生產(chǎn)出現(xiàn)事故問題排查困難,以及線程池參數(shù)難以定義

          文章圍繞線程池監(jiān)控展開,討論 線程池如何監(jiān)控、監(jiān)控的指標(biāo)以及監(jiān)控?cái)?shù)據(jù)的存儲(chǔ)展示




          01


          如何監(jiān)控運(yùn)行數(shù)據(jù)



          設(shè)想一下,如果想監(jiān)控線程池的運(yùn)行數(shù)據(jù),你會(huì)怎么操作?這里提供兩種常規(guī)思路

          1. 線程池運(yùn)行時(shí)埋點(diǎn),每一次運(yùn)行任務(wù)都進(jìn)行統(tǒng)計(jì)
          2. 定時(shí)獲取線程池的運(yùn)行數(shù)據(jù)

          這里我推薦第二種,因?yàn)榫€程池的監(jiān)控 API 會(huì)通過 獲取主鎖來控制結(jié)果的相對(duì)準(zhǔn)確性,性能相對(duì)較差,后面會(huì)詳細(xì)說明

          為什么叫相對(duì)準(zhǔn)確?因?yàn)槿蝿?wù)和線程的狀態(tài)在計(jì)算過程中可能會(huì)動(dòng)態(tài)變化,只能給到一個(gè)近似值,保證不了絕對(duì)準(zhǔn)確

          模擬下定時(shí)采集線程池運(yùn)行時(shí)數(shù)據(jù)的代碼

          private?ScheduledThreadPoolExecutor?collectVesselExecutor;

          String?collectVesselTaskName?=?"client.scheduled.collect.data";
          collectVesselExecutor?=?new?ScheduledThreadPoolExecutor(
          ????????new?Integer(1),
          ????????ThreadFactoryBuilder.builder().daemon(true).prefix(collectVesselTaskName).build()
          );

          //?延遲?initialDelay?后循環(huán)調(diào)用.?scheduleWithFixedDelay?每次執(zhí)行時(shí)間為上一次任務(wù)結(jié)束時(shí),?向后推一個(gè)時(shí)間間隔
          collectVesselExecutor.scheduleWithFixedDelay(
          ????????()?->?runTimeGatherTask(),
          ????????properties.getInitialDelay(),
          ????????properties.getCollectInterval(),
          ????????TimeUnit.MILLISECONDS
          );

          一般線程池分為兩種方式創(chuàng)建,Spring Bean 和非 Spring Bean,假設(shè)創(chuàng)建的線程池是 Spring 管理的

          我們只需要在 Spring 容器啟動(dòng)成功后,延遲一段時(shí)間后開始采集運(yùn)行數(shù)據(jù)就 OK 了

          不論線程池是否由 Spring 管理,采集的方式大致相同。一種從 Spring 容器取,一種是創(chuàng)建好線程池后放到一個(gè)自定義容器




          02


          監(jiān)控的指標(biāo)有哪些?



          說一下目前 Hippo4J 定義的線程池監(jiān)控指標(biāo),包括不限于。大家有業(yè)務(wù)中使用到的監(jiān)控指標(biāo)都可以討論下

          • 線程池當(dāng)前負(fù)載:當(dāng)前線程數(shù) / 最大線程數(shù)
          • 線程池峰值負(fù)載:當(dāng)前線程數(shù) / 最大線程數(shù),線程池運(yùn)行期間最大的負(fù)載
          • 核心線程數(shù):線程池的核心線程數(shù)
          • 最大線程數(shù):線程池限制同時(shí)存在的線程數(shù)
          • 當(dāng)前線程數(shù):當(dāng)前線程池的線程數(shù)
          • 活躍線程數(shù):執(zhí)行任務(wù)的線程的大致數(shù)目
          • 最大出現(xiàn)線程數(shù):線程池中運(yùn)行以來同時(shí)存在的最大線程數(shù)
          • 阻塞隊(duì)列:線程池暫存任務(wù)的容器
          • 隊(duì)列容量:隊(duì)列中允許元素的最大數(shù)量
          • 隊(duì)列元素:隊(duì)列中已存放的元素?cái)?shù)量
          • 隊(duì)列剩余容量:隊(duì)列中還可以存放的元素?cái)?shù)量
          • 線程池任務(wù)完成總量:已完成執(zhí)行的任務(wù)的大致總數(shù)
          • 拒絕策略執(zhí)行次數(shù):運(yùn)行時(shí)拋出的拒絕次數(shù)總數(shù)

          這些指標(biāo)可以幫助我們解決大多數(shù)因?yàn)榫€程池而導(dǎo)致的問題排查。但是,事情往往不能盡善盡美

          當(dāng)前線程數(shù)活躍線程數(shù)最大出現(xiàn)線程數(shù)線程池任務(wù)完成總量 的線程池 API 會(huì)先獲取到 mainLock,然后才開始計(jì)算

          mainLock 是線程池的主鎖,線程執(zhí)行、線程銷毀和線程池停止等都會(huì)使用到這把鎖

          final?ReentrantLock?mainLock?=?this.mainLock;
          mainLock.lock();
          try?{
          ????xxxxx
          }?finally?{
          ????mainLock.unlock();
          }

          如果頻繁獲取這把鎖,會(huì)導(dǎo)致原有線程池任務(wù)執(zhí)行性能受到影響

          所以,我們應(yīng)該避免頻繁獲取這幾項(xiàng)參數(shù),這也是不使用線程池任務(wù)執(zhí)行埋點(diǎn)最重要的原因




          03


          監(jiān)控?cái)?shù)據(jù)存儲(chǔ)



          上面的線程池監(jiān)控指標(biāo)如果只能支持實(shí)時(shí)查看,并不能幫忙開發(fā)日常排查錯(cuò)誤

          大部分場(chǎng)景下,生產(chǎn)上的問題發(fā)現(xiàn)會(huì)有延遲。比如 12:30 出現(xiàn)的問題,業(yè)務(wù)13:00 進(jìn)行的反饋

          為了更好幫助開發(fā)排錯(cuò),我們需要將線程池的歷史運(yùn)行數(shù)據(jù)進(jìn)行存儲(chǔ)

          說到線程池歷史運(yùn)行數(shù)據(jù)的存儲(chǔ),使用 時(shí)序數(shù)據(jù)庫(kù)(TSDB) 是最合適的

          但大部分情況下,公司不會(huì)為了這一個(gè)需求搭建或者采購(gòu)時(shí)序數(shù)據(jù)庫(kù),那就可以使用折中方案,比如說 MySQL、ES 等

          我們以 MySQL 為例,his_run_data?歷史運(yùn)行數(shù)據(jù)表,建表語(yǔ)句如下:

          CREATE?TABLE?`his_run_data`?(
          ??`thread_pool_id`?varchar(56)?DEFAULT?NULL?COMMENT?'線程池ID',
          ??`instance_id`?varchar(256)?DEFAULT?NULL?COMMENT?'實(shí)例ID',
          ??`current_load`?bigint(20)?DEFAULT?NULL?COMMENT?'當(dāng)前負(fù)載',
          ??`peak_load`?bigint(20)?DEFAULT?NULL?COMMENT?'峰值負(fù)載',
          ??`pool_size`?bigint(20)?DEFAULT?NULL?COMMENT?'線程數(shù)',
          ??`active_size`?bigint(20)?DEFAULT?NULL?COMMENT?'活躍線程數(shù)',
          ??`queue_capacity`?bigint(20)?DEFAULT?NULL?COMMENT?'隊(duì)列容量',
          ??`queue_size`?bigint(20)?DEFAULT?NULL?COMMENT?'隊(duì)列元素',
          ??`queue_remaining_capacity`?bigint(20)?DEFAULT?NULL?COMMENT?'隊(duì)列剩余容量',
          ??`completed_task_count`?bigint(20)?DEFAULT?NULL?COMMENT?'已完成任務(wù)計(jì)數(shù)',
          ??`reject_count`?bigint(20)?DEFAULT?NULL?COMMENT?'拒絕次數(shù)',
          ??`timestamp`?bigint(20)?DEFAULT?NULL?COMMENT?'時(shí)間戳',
          ??`gmt_create`?datetime?DEFAULT?NULL?COMMENT?'創(chuàng)建時(shí)間',
          ??`gmt_modified`?datetime?DEFAULT?NULL?COMMENT?'修改時(shí)間',
          ??PRIMARY?KEY?(`id`),
          ??KEY?`idx_group_key`?(`tp_id`,`instance_id`)?USING?BTREE,
          ??KEY?`idx_timestamp`?(`timestamp`)?USING?BTREE
          )?ENGINE=InnoDB?AUTO_INCREMENT=1?DEFAULT?CHARSET=utf8mb4?COMMENT='歷史運(yùn)行數(shù)據(jù)表';

          可以看到,建表語(yǔ)句中有三個(gè)關(guān)鍵字段:

          thread_pool_id:表示當(dāng)前數(shù)據(jù)的線程池標(biāo)識(shí)

          instance_id:應(yīng)用可能集群部署,標(biāo)識(shí)集群下唯一的線程池

          timestamp:記錄線程池運(yùn)行數(shù)據(jù)產(chǎn)生時(shí)的時(shí)間戳

          有一個(gè)問題,線上的線程池是源源不斷產(chǎn)生運(yùn)行數(shù)據(jù)的,遲早不得把表的數(shù)據(jù)量推到上億?

          因?yàn)閿?shù)據(jù)是有時(shí)效性的,過了一定時(shí)間之后,就沒有必要再占用實(shí)時(shí)的資源

          針對(duì)上述問題提供兩種解決方案:

          1. 假設(shè)數(shù)據(jù)存儲(chǔ) 1 天,如果超出這個(gè)時(shí)間,直接刪除即可
          2. 同上所述,過期數(shù)據(jù)可以保留到備份表中,并刪除 his_run_data 數(shù)據(jù)

          可能有的小伙伴還會(huì)擔(dān)心,數(shù)據(jù)量太大會(huì)不會(huì)導(dǎo)致查詢時(shí)過慢?

          我們可以算一下,假設(shè)有 100 個(gè)應(yīng)用,每個(gè)應(yīng)用部署 10 個(gè)節(jié)點(diǎn)

          假設(shè)數(shù)據(jù)有效期為 1 小時(shí),那么可以產(chǎn)出的數(shù)據(jù)是 72 萬(wàn),一天也就是 1728 萬(wàn)

          對(duì)于 MySQL 而言,幾千萬(wàn)數(shù)據(jù)量以下針對(duì)索引的查詢,都不會(huì)產(chǎn)生性能瓶頸




          04


          如何定義公共監(jiān)控?



          抽象線程池存儲(chǔ)

          上面說到,線程池的采集歷史運(yùn)行數(shù)據(jù)在各個(gè)應(yīng)用系統(tǒng)中,數(shù)據(jù)的存儲(chǔ)、定期刪除是否可以抽象出來,避免重復(fù)的工作

          如果選擇抽象數(shù)據(jù)存儲(chǔ),客戶端節(jié)點(diǎn)與服務(wù)端之間的交互如下:

          1. 客戶端定時(shí)采集線程池歷史運(yùn)行數(shù)據(jù),將數(shù)據(jù)打包好發(fā)送服務(wù)端
          2. 服務(wù)端接收客戶端上報(bào)的數(shù)據(jù),進(jìn)行數(shù)據(jù)入庫(kù)持久化存儲(chǔ)
          3. 服務(wù)端定期刪除或存檔客戶端線程池歷史運(yùn)行數(shù)據(jù)
          4. 由服務(wù)端統(tǒng)一對(duì)外提供線程池運(yùn)行圖表的數(shù)據(jù)展示

          這里有個(gè)小問題,客戶端如何打包發(fā)送給服務(wù)端?定時(shí)采集數(shù)據(jù)后直接上報(bào)是不是可行呢

          不推薦采集、上報(bào)兩種行為放到一個(gè)流程中,好的設(shè)計(jì)應(yīng)該是要 分離開職責(zé);而且,如果在上報(bào)過程中網(wǎng)絡(luò)出現(xiàn)阻塞等等問題,會(huì)耽誤采集線程的下一次采集結(jié)果

          我們可以使用多線程生產(chǎn)、消費(fèi)模型來做,相信大家初學(xué)多線程一定都學(xué)過這個(gè)設(shè)計(jì)

          //?緩沖隊(duì)列
          private?BlockingQueue?messageCollectVessel??=?new?ArrayBlockingQueue(bufferSize);

          //?生產(chǎn)者
          Message?message?=?collector.collectMessage();
          boolean?offer?=?messageCollectVessel.offer(message);
          if?(!offer)?{
          ????log.warn("Buffer?data?starts?stacking?data...");
          }

          //?消費(fèi)者
          while?(true)?{
          ????try?{
          ????????Message?message?=?messageCollectVessel.take();
          ????????messageSender.send(message);
          ????}?catch?(Throwable?ex)?{
          ????????log.error("Consumption?buffer?container?task?failed.?Number?of?buffer?container?tasks?::?{}",?messageCollectVessel.size(),?ex);
          ????}
          }

          創(chuàng)建阻塞緩沖隊(duì)列,由定時(shí)線程池采集歷史運(yùn)行數(shù)據(jù),并放到緩沖隊(duì)列中;然后起一個(gè)線程,循環(huán)消費(fèi)即可

          極端情況下緩沖隊(duì)列元素會(huì)出現(xiàn)堆積,最新采集的線程池?cái)?shù)據(jù)也就無(wú)法插入成功,為了不影響客戶端的運(yùn)行,僅做異常警告處理

          使用最新抽象出來的客戶端、服務(wù)端交互流程,有以下幾個(gè)優(yōu)點(diǎn)

          1. 數(shù)據(jù)的存儲(chǔ)和查詢展示由服務(wù)端提供功能,減輕客戶端壓力和重復(fù)工作量
          2. 歷史運(yùn)行數(shù)據(jù)的刪除或備份操作由服務(wù)端統(tǒng)一執(zhí)行
          3. 不同的項(xiàng)目不需要為線程池歷史運(yùn)行數(shù)據(jù)分別創(chuàng)建表結(jié)構(gòu)存儲(chǔ)
          4. 形成交互規(guī)范,避免業(yè)務(wù)發(fā)散單獨(dú)開發(fā),中心化的設(shè)計(jì)更利于技術(shù)的迭代和管理

          監(jiān)控圖表展示?

          不同公司對(duì)于線程池的監(jiān)控不盡相同,出于各種考慮,會(huì)將監(jiān)控封裝成最符合自己業(yè)務(wù)場(chǎng)景的流程

          Hippo4J 從最基本的指標(biāo)出發(fā),封裝出了最小代價(jià)的監(jiān)控體系,并提供可視化頁(yè)面的圖標(biāo)展示

          有興趣可以查看 Hippo4J 框架官網(wǎng)介紹

          Site:https://www.hippox.cn

          還有一個(gè)功能點(diǎn),考慮到很多公司搭建了一套監(jiān)控體系,其中以 Prometheus + Grafana 為主

          后續(xù) Hippo4J 會(huì)接入 Prometheus,應(yīng)用內(nèi)部存儲(chǔ)線程池的運(yùn)行數(shù)據(jù),適配 Prometheus 采集存儲(chǔ),最終展示到 Grafana




          05


          總結(jié)回顧



          線程池作為企業(yè)級(jí)應(yīng)用廣泛的技術(shù),對(duì)它的監(jiān)控是不可或缺的穩(wěn)定性保障之一

          文章從線程池的監(jiān)控出發(fā),講解了如何監(jiān)控、監(jiān)控的指標(biāo)以及監(jiān)控?cái)?shù)據(jù)的存儲(chǔ),相信讀者們也各有收獲

          看了上面的線程池監(jiān)控內(nèi)容,大家有什么想要補(bǔ)充的,在下方評(píng)論區(qū)留言

          各位讀者所在的公司又是如何對(duì)線程池監(jiān)控,可以互相交流下心得


          瀏覽 40
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  天天日天天射一区二区三区 | 五月天婷婷爱 | 免费大香蕉 | 91av成人在线播放 | 大香蕉情色 |