自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

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

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

大部分情況下,線程池的運(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ī)思路

線程池運(yùn)行時(shí)埋點(diǎn),每一次運(yùn)行任務(wù)都進(jìn)行統(tǒng)計(jì)

定時(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ù)的代碼

  1. private ScheduledThreadPoolExecutor collectVesselExecutor; 
  2.  
  3. String collectVesselTaskName = "client.scheduled.collect.data"
  4. collectVesselExecutor = new ScheduledThreadPoolExecutor( 
  5.         new Integer(1), 
  6.         ThreadFactoryBuilder.builder().daemon(true).prefix(collectVesselTaskName).build() 
  7. ); 
  8.  
  9. // 延遲 initialDelay 后循環(huán)調(diào)用. scheduleWithFixedDelay 每次執(zhí)行時(shí)間為上一次任務(wù)結(jié)束時(shí), 向后推一個(gè)時(shí)間間隔 
  10. collectVesselExecutor.scheduleWithFixedDelay( 
  11.         () -> runTimeGatherTask(), 
  12.         properties.getInitialDelay(), 
  13.         properties.getCollectInterval(), 
  14.         TimeUnit.MILLISECONDS 
  15. ); 

一般線程池分為兩種方式創(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ì)使用到這把鎖

  1. final ReentrantLock mainLock = this.mainLock; 
  2. mainLock.lock(); 
  3. try { 
  4.     xxxxx 
  5. } finally { 
  6.     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ǎ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ù)庫(TSDB) 是最合適的

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

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

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

可以看到,建表語句中有三個(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ì)上述問題提供兩種解決方案:

  • 假設(shè)數(shù)據(jù)存儲(chǔ) 1 天,如果超出這個(gè)時(shí)間,直接刪除即可
  • 同上所述,過期數(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 萬,一天也就是 1728 萬

對(duì)于 MySQL 而言,幾千萬數(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ù)端之間的交互如下:

  • 客戶端定時(shí)采集線程池歷史運(yùn)行數(shù)據(jù),將數(shù)據(jù)打包好發(fā)送服務(wù)端
  • 服務(wù)端接收客戶端上報(bào)的數(shù)據(jù),進(jìn)行數(shù)據(jù)入庫持久化存儲(chǔ)
  • 服務(wù)端定期刪除或存檔客戶端線程池歷史運(yùn)行數(shù)據(jù)
  • 由服務(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ì)

  1. // 緩沖隊(duì)列 
  2. private BlockingQueue<Message> messageCollectVessel  = new ArrayBlockingQueue(bufferSize); 
  3.  
  4. // 生產(chǎn)者 
  5. Message message = collector.collectMessage(); 
  6. boolean offer = messageCollectVessel.offer(message); 
  7. if (!offer) { 
  8.     log.warn("Buffer data starts stacking data..."); 
  9.  
  10. // 消費(fèi)者 
  11. while (true) { 
  12.     try { 
  13.         Message message = messageCollectVessel.take(); 
  14.         messageSender.send(message); 
  15.     } catch (Throwable ex) { 
  16.         log.error("Consumption buffer container task failed. Number of buffer container tasks :: {}", messageCollectVessel.size(), ex); 
  17.     } 

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

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

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

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

監(jiān)控圖表展示

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

Hippo4J 從最基本的指標(biāo)出發(fā),封裝出了最小代價(jià)的監(jiān)控體系,并提供可視化頁面的圖標(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ǔ),相信讀者們也各有收獲

 

責(zé)任編輯:武曉燕 來源: 龍臺(tái)的技術(shù)筆記
相關(guān)推薦

2013-09-29 10:04:09

Arrownock開發(fā)者社交

2015-07-22 16:08:46

OpenStack開源貢獻(xiàn)代碼

2012-05-21 22:04:02

Android

2014-04-08 09:58:26

PythonPython教程

2012-08-22 09:39:28

開發(fā)者

2014-12-24 10:00:07

Spring

2024-12-12 09:00:33

2013-07-15 14:08:10

開發(fā)者技能

2021-02-01 15:59:41

Angular開發(fā)者代碼

2011-06-22 10:35:02

FirefoxWeb

2019-12-20 09:12:37

內(nèi)存工程師網(wǎng)絡(luò)

2013-12-27 09:03:47

開發(fā)項(xiàng)目

2020-10-09 09:44:25

JavaScript 開發(fā) 應(yīng)用

2024-07-22 08:00:00

2021-12-25 22:31:55

Sentry 監(jiān)控SDK 開發(fā) 性能監(jiān)控

2022-08-15 11:29:44

騰訊云云端開發(fā)工具Web IDE

2022-07-05 08:25:10

Reactyarn link

2019-06-27 10:15:46

架構(gòu)代碼項(xiàng)目

2012-09-05 14:45:45

Windows 8

2015-03-27 11:26:58

移動(dòng)開ISVSI
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)