【连载】跨越时代的度量衡——Pandora.js 的 Metrics 介绍
自秦始皇統(tǒng)一六國,天下歸一,推行“一法度衡石丈尺,車同軌 ,書同文”,頒發(fā)統(tǒng)一度量衡詔書,制定了一套嚴格的管理制度,天底下的度量衡就變成了一套。而如今程序世界也是天下分崩離析,不同編程語言各占一隅,不過即使語法不同,但是分分合合,思路終歸一致,想要度量代碼的心情依舊是一樣的。
度量的作用
很多同學表示懷疑,為什么要度量?
其實回答很簡單,度量就像是身體健康的檢查器,就像體檢給的報告,沒有這份報告,你是不是很擔心自己的身體有沒有出問題,要是好幾年沒體檢,恐怕焦慮癥都要犯了。
平常我們所說的監(jiān)控報警,其實就是度量的一種具體應用和泛化,其實還有更大更廣泛作用,比如:
當你上線了一個應用,你怎么知道應用是健康的?
當你發(fā)布了一個新功能,你怎么知道這個功能對線上有哪些影響,怎么去評估呢?
當你升級了一個包版本,你怎么知道他是穩(wěn)定的呢?
一切的一切,盡在度量上。
Pandora.js 的度量體系參考了 spring-boot 的命名習慣,以及結合了阿里內(nèi)部的 alimetrics 體系,加上業(yè)界的 opentracing 鏈路追蹤模型,形成了一整套可檢查,可度量,可追蹤的完整方案。
我們今天來細說 Pandora.js 其中的一種度量機制,這不僅是 Node.js 的度量,也是程序通用的度量 - Metrics。
Metric 的命名
Metric 的目標是可自我描述,所以在命名上會盡可能的根據(jù)業(yè)務場景來命名。
在參考了業(yè)界的metrics規(guī)范,以及結合了阿里集團內(nèi)使用場景之后,我們使用了基礎的 MetricName 對象格式做為 Metric 的名字。
Metrics 是復數(shù)形式,目前這些度量在業(yè)界口頭表述都可能叫 Metrics ,而實際在代碼中對特定的單個指標叫 Metric,而普通的非特定單數(shù)形式繼續(xù)沿用 Metrics 的表述 。
MetricName 大體分為兩部分,key 和 tags。
key 代表著一個具體的項,比如有一個 metrics 指標是某個 HTTP 接口,那么這個指標的 key 就可能是 application.http.request.path,通過 . 將命令進一步的縮小,每個公司可以有自己不同的規(guī)范,根據(jù)部門、產(chǎn)品、功能來進行劃分,盡量做到 key 可描述,可擴展,所有的單詞用下劃線 '_'連接,字母采用小寫形式。
tags 代表著一個指標的不同分類,它和 key 加起來唯一指定了一個 Metric。tags 是一個對象 {},通過不同的 kv 對來描述詳情,比如區(qū)分不同請求的來源,繼續(xù)以 HTTP 接口來舉例, {"source":"shanghai"} 和 {"source":"hangzhou"} 這樣就是不同的 tags,結合 key,就用來表示不同的 Metric 了。
這樣的好處就是 tags 可以無限擴展,不會影響到 key,同時,在后續(xù)的存儲中,同一個 key 可以進行查詢篩選,保證數(shù)據(jù)一致性和連貫性。
具體的實例我們將在之后的實例中介紹。
對外的數(shù)據(jù)格式
除了定義 Metric 的名字之外,我們還需要考慮對外輸出的格式,既然是 Node 體系,我們首先考慮的自然是 JSON 結構的格式,便于閱讀以及數(shù)據(jù)格式化存儲。
基于 MetricName,我們將名字和值定義了成了通用的 MetricObject 格式。
一個標準的輸出格式大概如下:
{ "metric": "sys.cpu.nice", "timestamp": 1346846400, "value": 18, "type": "COUNTER", "level": "CRITICAL", "tags": { "host": "web01", "dc": "lga" } }和 MetricName 類似,包含一些常用信息,包括 key,value,時間戳,tags 等。
最終這個數(shù)據(jù)格式會被內(nèi)置的 Reporter 體系輸出到不同的對外接口中,包括文本文件,HTTP 接口等等,這樣外部系統(tǒng)根據(jù)這樣的內(nèi)容進行存儲,計算,分發(fā),來進行最后的監(jiān)控,可視化工作,這個不在我們這個體系內(nèi),暫時不做過多的介紹了。
Metrics 的類型
Metric 除了有名字,還有類型,目前最常用的就是瞬時值和計數(shù)器,此外還有其他的一些。
在了解業(yè)界實踐并結合集團內(nèi)部的實踐基礎上,我們抽象出以下幾種度量場景:
累加型度量:對指標的數(shù)據(jù)進行累加,反映的是數(shù)據(jù)隨著時間單調遞增的關系,應用接受到的 HTTP 請求的總次數(shù)
瞬態(tài)型度量:表示指標在當前時間點的瞬時情況,反映的是數(shù)據(jù)隨著時間上下波動的關系,如系統(tǒng)的load,內(nèi)存使用率,堆信息等
變化速率度量:表示指標在某個時間段內(nèi)變化的速率,反映的是數(shù)據(jù)隨時間的增長快慢關系,如某個接口的 QPS
數(shù)據(jù)分布度量:表示某一些指標在某個時間段內(nèi)的分布情況,反映的是數(shù)據(jù)隨時間的統(tǒng)計學分布關系,如某段時間內(nèi),某個接口的 RT 的最大,最小,平均值,方差,95% 分位數(shù)等
基于這些場景,結合業(yè)界的 Metrics 實現(xiàn),我們目前提供四種最基礎的指標,即:
Gauge 瞬態(tài)的度量指標
Counter 累加計數(shù)型指標
Histogram 分布度量指標
Meter 速率度量指標
目前最常用的是 Gauge 瞬態(tài)值以及 Counter 累加值,80% 的場景都可以覆蓋
這樣在不同的場景下,我們都可以找到相應的 Metric 類型了。在某些場景下,我們還做了額外的一些指標類型(在阿里內(nèi)部還有兩種聚合的類型,等機會開源)
Metrics 數(shù)據(jù)生產(chǎn)
目前 Pandora.js 全部使用 TypeScript 來編寫,有些代碼必須帶類型定義。
所有的 Metric 類型都繼承與 Metric 接口
瞬態(tài)型度量
大部分的度量都從瞬態(tài)值 Gauge 介紹起,因為它最簡單,最直觀的表示數(shù)據(jù)的真實情況,也不涉及時間間隔的問題。
Gauge 只包含一個 getValue 方法,只需要實現(xiàn)這個方法即可,比如,你想要知道當前進程的 CPU 使用情況,就可以一句話解決。
<BaseGauge> { getValue() { const startUsage = process.cpuUsage(); return startUsage.user; } }注意,所有的 Metrics 最終輸出的一定是數(shù)字形式,這樣才可度量,如果你希望輸出的是字符串類的信息,我們有另一套輸出體系,這將在之后的文章介紹。
累加型度量
Counter 是第二個介紹的類型,計數(shù)器和 Gauge 不太一樣,它是累加型,適用于記錄調用總量等類型的數(shù)據(jù),比如某個接口的調用次數(shù)。
如下圖是計數(shù)器的繼承接口和實現(xiàn)類。
除了基礎的 BaseCounter 實現(xiàn)之外,我們提供了 BucketCounter 分桶計數(shù)器。
分桶計數(shù)的原理是定義一個時間間隔,將一段時間按照時間間隔分割為幾個桶,每個桶保存當前時間間隔的計數(shù)。
比如時間間隔為 5s ,桶的總數(shù)為 10 個,那么 0~5s 為一個桶,5~10s 為下一個,以此類推。當計數(shù)的執(zhí)行的時間為 2s 時,那么將在第一桶中累加,如果為 7s 時,那么將在第二個桶累加,非常容易理解。
在實際場景中,因為內(nèi)存限制,不宜保存過多,桶的量會有限制,采用環(huán)形隊列存儲同時避免數(shù)據(jù)的挪動。
舉個常用例子,記錄 Koa 服務的請求數(shù)。
// 實際使用需要從 MetricsClient 拿到 BucketCounter let counter = new BucketCounter(); app.use(async (ctx, next) => { // 累加 1 counter.inc(1); counter.inc(); await next() });分布度量
第三個介紹的是 Histogram,直方分布指標,Pandora.js 包含一個基礎實現(xiàn)類 BaseHistogram, 通過它可以用于統(tǒng)計某個接口的響應時間,可以展示 50%, 70%, 90% 的請求響應時間落在哪個區(qū)間內(nèi),通過這些你可以計算出 Apdex。
這邊的分布暫時只考慮單機分布,在集群維度上不能這樣計算。
對于分布計算,核心就是維護一個數(shù)據(jù)集 Reservoir ,數(shù)據(jù)集用來提供數(shù)據(jù)存儲以及獲取當前快照的能力。這其中最重要的就是數(shù)據(jù)更新的策略,目前 Pandora.js 只實現(xiàn)了隨機采樣(UniformReservoir)和 指數(shù)衰減隨機采樣(ExponentiallyDecayingReservoir)的實現(xiàn),由于隨機采樣并不能很好的表現(xiàn)權重問題,默認的是指數(shù)衰減隨機采樣,其他的采樣算法沒有實現(xiàn),有興趣的同學可以補充。
舉個常用例子,記錄 Koa 服務的成功比率,采用隨機采樣算法,間隔 1s,2個分桶,展示獲取了平均數(shù)等信息。
// 實際使用需要從 MetricsClient 拿到 BaseHistogram let histogram = new BaseHistogram(ReservoirType.UNIFORM, 1, 2); app.use(async (ctx, next) => { histogram.update(10); histogram.update(20); // other biz }); // let snapshot = histogram.getSnapshot(); // expect(snapshot.getMean()).to.equal(15); // expect(snapshot.getMax()).to.equal(20); // expect(snapshot.getMin()).to.equal(10); // expect(snapshot.getMedian()).to.equal(15);變化速率度量
第四個介紹的是 Meter,是一種用于度量一段時間內(nèi)吞吐率的計量器。例如,一分鐘內(nèi),五分鐘內(nèi),十五分鐘內(nèi)的qps指標。
這里要指出,變化的速率,我們一般情況下會關心兩個地方,一個是瞬時爆發(fā),超出平常正常值非常高的這樣的波動變化,另一個是一段時間內(nèi)的趨勢,從平均的角度來看整體度量的一種方式,這種方式會將高低點進行平均來看。
前一種在 Metrics 中使用 Rate 的概念,只記錄事件的累計總次數(shù),有外部系統(tǒng)來通過前后兩次采集,來計算瞬時速率,這里我們稱之為 Rate。
在 rate 的計算中,我們認為數(shù)據(jù)的增長是 線性的。其計算方式為:rate = (v2 - v1) / (t2 - t1),其中時間的單位是 s。
這樣的好處是,通過調整采集頻率,可以支持任意時間間隔的瞬時速率計算。但缺點是,當兩次采樣之間系統(tǒng)重啟的時候,會計算出負數(shù),同時會有一部分數(shù)據(jù)丟失。
后一種通過指數(shù)移動加權平均(Exponential Weighted Moving Average, EWMA)來計算。
針對速率型度量指標,我們提供了 1 分鐘(m1),5 分鐘(m5),15分鐘的EWMA(m15),分別用于反映距離當前時間點 1 分鐘,5 分鐘,15 分鐘的速率變化。
其具體的計算方法,和 Linux 系統(tǒng)中 load1, load5, load15 的計算方法完全一致。即,每 5 秒鐘統(tǒng)計一次瞬時速率,并應用于如下的遞推公式:
EWMA(t) = EWMA(t-1) + alpha * (instantRate - EWMA(t-1))其中 alpha 取值范圍為 0~1, 稱為衰減系數(shù),該系數(shù)越大,則距離當前的時間點越老的數(shù)據(jù)權重衰減的越快。
舉個常用例子,記錄 Koa 某個路由的調用比率。
// 實際使用需要從 MetricsClient 拿到 BaseMeter let meter = new BaseMeter(); router.get('/home', async (ctx) => { // 接口調用埋點 meter.mark(1); }); // meter.getMeanRate(); 總數(shù)除以時間 // meter.getOneMinuteRate(); // 一分鐘的 EWMA本文最后
以上只是 Pandora.js 的度量體系的一部分,結合了阿里自己的 Metrics 體系,只能管中窺豹,簡單的介紹一下幾種最基本的度量指標類型,通過這本的度量器,我們可以將數(shù)據(jù)從業(yè)務代碼中產(chǎn)生出來。
不過這僅僅是數(shù)據(jù)生成,除此之外,數(shù)據(jù)采集和加工也非常的重要,下一篇我們將會講到,Pandora.js 的數(shù)據(jù)采集和加工部分。
Pandora.js 項目地址:https://github.com/midwayjs/pandora 歡迎社會各界前來 Star ~
總結
以上是生活随笔為你收集整理的【连载】跨越时代的度量衡——Pandora.js 的 Metrics 介绍的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: js 对象深拷贝、对象数组深拷贝的几种方
- 下一篇: 钉钉ppt放映显示备注_[备注] 钉钉使