javascript
node.js request get 请求怎么拿到返回的数据_NodeJS运维: 从 0 开始 Prometheus + Grafana 业务性能指标监控...
為什么需要指標監控告警
一個復雜的應用,往往由很多個模塊組成,而且往往會存在各種各樣奇奇怪怪的使用場景,誰也不能保證自己維護的服務永遠不會出問題,等用戶投訴才發現問題再去處理問題就為時已晚,損失已無法挽回。
所以,通過數據指標來衡量一個服務的穩定性和處理效率,是否正常運作,監控指標曲線的狀態,指標出現異常時及時主動告警,這一套工具就十分重要。
常見的一些指標,包括但不限于: - QPS - 請求處理耗時 - 進程占用內存 - 進程占用CPU - golang 服務的 goroutine - nodejs 的 event loop lag - 前端應用的 Performance 耗時 - ...
舉個例子,假如一個服務: - 使用內存隨著時間逐漸上漲 - CPU 占用越來越高 - 請求耗時越來越高,請求成功率下降 - 磁盤空間頻頻被擠爆
又或者一個前端單頁面應用: - 前端重定向到 /error 頁,/excption 頁的次數越來越多 - 某個頁面打開次數越來越少 - 某個系統/某個版本的設備的激活率越來越低 - ...
這到底是人性的扭曲還是道德的淪喪 一旦應用存在某些缺陷導致這些問題,通過服務日志,很難直觀快速地察覺到這些指標的變化波動。
而對于前端,則很可能根本無法感知到用戶的行為。
通過監控和告警手段可以有效地覆蓋了「發現」和「定位」問題,從而更有效率地排查和解決問題。
指標監控系統:Prometheus
Prometheus 是一個開源的服務監控系統和時間序列數據庫。
工作流可以簡化為: 1. client 采集當前 機器/服務/進程 的狀態等相關指標數據 2. Prometheus server 按一定的時間周期主動拉取 client 的指標數據,并存儲到時序數據庫中 3. 發現指標異常后,通過 alert manager 將告警通知給相關負責人
具體的架構設計如下:
為什么不用 mysql 存儲?
Prometheus 用的是自己設計的時序數據庫(TSDB),那么為什么不用我們更加熟悉,更加常用的 mysql, 或者其他關系型數據庫呢?
假設需要監控 WebServerA 每個API的請求量為例,需要監控的維度包括:服務名(job)、實例IP(instance)、API名(handler)、方法(method)、返回碼(code)、請求量(value)。
如果以SQL為例,演示常見的查詢操作:
# 查詢 method=put 且 code=200 的請求量 SELECT * from http_requests_total WHERE code=”200” AND method=”put” AND created_at BETWEEN 1495435700 AND 1495435710;# 查詢 handler=prometheus 且 method=post 的請求量 SELECT * from http_requests_total WHERE handler=”prometheus” AND method=”post” AND created_at BETWEEN 1495435700 AND 1495435710;# 查詢 instance=10.59.8.110 且 handler 以 query 開頭 的請求量 SELECT * from http_requests_total WHERE handler=”query” AND instance=”10.59.8.110” AND created_at BETWEEN 1495435700 AND 1495435710;通過以上示例可以看出,在常用查詢和統計方面,日常監控多用于根據監控的維度進行查詢與時間進行組合查詢。如果監控100個服務,平均每個服務部署10個實例,每個服務有20個API,4個方法,30秒收集一次數據,保留60天。那么總數據條數為:100(服務)* 10(實例)* 20(API)* 4(方法)* 86400(1天秒數)* 60(天) / 30(秒)= 138.24 億條數據,寫入、存儲、查詢如此量級的數據是不可能在Mysql類的關系數據庫上完成的。 因此 Prometheus 使用 TSDB 作為 存儲引擎。
時序數據庫(Time Series Database/TSDB)
時序數據庫主要用于指處理帶時間標簽(按照時間的順序變化,即時間序列化)的數據,帶時間標簽的數據也稱為時序數據。
對于 prometheus 來說,每個時序點結構如下: - metric: 指標名,當前數據的標識,有些系統中也稱為name。 - label: 標簽屬性 - timestamp: 數據點的時間,表示數據發生的時間。 - value: 值,數據的數值
每個指標,有多個時序圖;多個時序數據點連接起來,構成一個時序圖假如用傳統的關系型數據庫來表示時序數據,就是以下結構:
create_time |__metric_name__ | path | value ----|----|------|---- 2020-10-01 00:00:00 |http_request_total | /home | 100 2020-10-01 00:00:00 |http_request_total | /error | 0 2020-10-01 00:00:15 |http_request_total | /home | 120 2020-10-01 00:01:00 |http_request_total | /home | 160 2020-10-01 00:01:00 |http_request_total | /error | 1
指標 request_total{path="/home"} 在 2020-10-01 00:01:00 時的 qps = (160 - 100)/60 = 1 , 同理,
指標 request_total{path="/error"} 在 2020-10-01 00:01:00 時的 qps = 1/60
相比于 MySQL,時序數據庫核心在于時序,其查詢時間相關的數據消耗的資源相對較低,效率相對較高,而恰好指標監控數據帶有明顯的時序特性,所以采用時序數據庫作為存儲層
數據類型
- counter: 計數器,只能線性增加,不斷變大,場景:qps
- gauge:絕對值,非線性,值可大可小,場景:機器溫度變化,磁盤容量,CPU 使用率,
- histogram:,聚合數據查詢耗時分布【服務端計算,模糊,不精確】
- summary:不能聚合查詢的耗時分布【客戶端計算,精確】
nodejs 指標采集與數據拉取
- 定義一個 Counter 的數據類型,記錄指標
- 定義訪問路徑為 /metrics 的controller
- Prometheus 主動請求 node client 的 /metrics 接口
promQL
promQL 是 prometheus 的查詢語言,語法十分簡單
基本查詢
查詢指標最新的值:
{__name__="http_request_total", handler="/home"}# 語法糖: http_request_total{handler="/home"}# 等價于 mysql: select * from http_request_total where handler="/home" ANDcreate_time=《now()》區間時間段查詢
查詢過去一分鐘內的數據
# promQL http_request_total[1m]# 等價于 SELECT * from http_requests_total WHERE create_time BETWEEN 《now() - 1min》 AND 《now()》;時間偏移查詢
PS: promQL 不支持指定時間點進行查詢,只能通過 offset 來查詢歷史某個點的數據查詢一個小時前的數據。
# promQL http_request_total offset 1h# 等價于 SELECT * from http_requests_total WHERE create_time=《now() - 1 hour》;promQL 查詢函數
根據以上的查詢語法,我們可以簡單組合出一些指標數據:
例如,查詢最近一天內的 /home 頁請求數
http_request_total{handler="/home"} - http_request_total{handler="/home"} offset 1d那么實際上面這個寫法很明顯比較不簡潔,我們可使用內置 increase 函數來替換:
# 和上述寫法等價 increase(http_request_total{handler="/home"}[1d])除了 increase 外,還有很多其他好用的函數,例如,
rate 函數計算 QPS
指標聚合查詢
除了上述基礎查詢外,我們可能還需要聚合查詢
假如我們有以下數據指標:
credit_insight_spl_id_all_pv{url="/home",channel="none"} credit_insight_spl_id_all_pv{url="/home",channel="mepage"} credit_insight_spl_id_all_pv{url="/error",channel="none"} credit_insight_spl_id_all_pv{url="/error",channel="mepage"}將所有指標數據以某個維度進行聚合查詢時,例如:查詢 url="/home" 最近一天的訪問量,channel 是 none還是mepage 的 /home 訪問量都包括在內。
我們理所當然地會寫出:
increase(credit_insight_spl_id_all_pv{url="/home"}[1d])但實際上我們會得出這樣的兩條指標結果:
credit_insight_spl_id_all_pv{url="/home",channel="none"} 233 credit_insight_spl_id_all_pv{url="/home",channel="mepage"} 666并非我們預期中的:
credit_insight_spl_id_all_pv{url="/home"} 899而要是我們想要得到這樣的聚合查詢結果,就需要用到 sum by
# 聚合 url="/home" 的數據 sum(increase(credit_insight_spl_id_all_pv{url="/home"}[1d])) by (url) # 得出結果: credit_insight_spl_id_all_pv{url="/home"} 899 # 所有 channel 中 /home 頁訪問量累加值# 聚合所有的 url 則可以這樣寫: sum(increase(credit_insight_spl_id_all_pv{}[1d])) by (url) # 得出結果: credit_insight_spl_id_all_pv{url="/home"} 899 credit_insight_spl_id_all_pv{url="/error"} 7# 等價于 mysql SELECT url, COUNT(*) AS total FROM credit_insight_spl_id_all_pv WHERE create_time between <now() - 1d> and <now()> GROUP BY url;指標時序曲線
以上的所有例子的查詢數值,其實都是最近時間點的數值,
而我們更關注的是一個時間段的數值變化。
要實現這個原理也很簡單,只需要在歷史的每個時間點都執行一次指標查詢,
# 假如今天7號 # 6號到7號的一天訪問量 sum(increase(credit_insight_spl_id_all_pv{}[1d] )) by (url) # 5號到6號的一天訪問量 offset 1d sum(increase(credit_insight_spl_id_all_pv{}[1d] offset 1d)) by (url) # 4號到5號的一天訪問量 sum(increase(credit_insight_spl_id_all_pv{}[1d] offset 2d)) by (url)而 Prometheus 已經內置了時間段查詢功能,并對此優化處理。
可通過 /api/v1/query_range 接口進行查詢,獲的 grpah:
Prometheus 查詢瓶頸
數據存儲: 指標數據有 “Writes are vertical,reads are horizontal” 的(垂直寫,水平讀)模式:
“Writes are vertical,reads are horizontal” 的意思是 tsdb 通常按固定的時間間隔收集指標并寫入,會 “垂直” 地寫入最近所有時間序列的數據,而讀取操作往往面向一定時間范圍的一個或多個時間序列,“橫向” 地跨越時間進行查詢
- 每個指標(metric)根據指標數量不同,有 labelA * labelB * labelC * ... 個時序圖
- 每個時序圖(time series)的一個點時序是 [timestamp, value], 例如 [1605607257, 233]。[時間戳-值] 可以確定圖上的一個點,一個時間區間內的所有點連成一個時序曲線圖。
- 因為 Prometheus 每隔 15s 采集一次數據,所以 時序點的時間間距是 15s,即1分鐘有60/15=4個時序點,1小時就有 4 * 60 = 240 個時序點。
而 Prometheus 的默認查詢 sample 上限是 5000w
所以,如果指標的時序圖數量過大,允許查詢的時間區間相對就會較小了。
一個圖表查詢時序數量的影響因素有 3 個,分別是: 1. 查詢條件的時序數量(n) 2. 查詢的時間區間(time) 3. 圖表曲線每個時序點之間的間隔(step)
以 credit_insight_spl_id_all_pv 指標為例,該指標總共大約有 n = 163698 種時序,
假如 step = 15s,如果搜索該指標過去 time = 60m 的全部時序圖,那么,需要搜索的例子要 163698 * 60 * (60/15) = 39287520,將近 4kw,是可以搜出來的。
但如果搜的是過去 90m 的數據,163698 * 90 * 4 = 58931280,超過了 5000w,你就發現數據請求異常: Error executing query: query processing would load too many samples into memory in query execution
所以,目測可得一個圖的查詢時序點數量公式是:total = n * time / step, time 和 step 的時間單位必須一致,total 必須不超過 5000w。
反推一下得出,time < 5000w / n * step 。要擴大搜索時間范圍,增大 step ,或者降低 n 即可做到。
- step 不變, 降低 n 【指定label值可減少搜索條件的結果數】 : credit_insight_spl_id_all_pv{systemType="Android", systemVersion="10"},n = 18955
- 增大 step 到 30s, n 不變:
當然,一般情況下,我們的 n 值只有幾百,而 step 基本是大于 60s 的,所以一般情況下都能查詢 2 個多月以上的數據圖。
可視化平臺: Grafana
grafana 是一個開源的,高度可配置的數據圖表分析,監控,告警的平臺,也是一款前端可視化的產品。
自定義圖表
grafana 內置提供多種圖表模板,具體是以下類型:
Prometheus 作為數據源的情況下,一般用的 graph 類型畫時序圖比較多。
對于一些基礎的數據大盤監控,這些圖表類型已經足夠滿足我們的需求。
但對于復雜的需求,這些類型無法滿足我們的需要時,我們安裝 pannel 插件,來更新可用的圖表類型,也可以根據官方文檔 build a panel plugin 開發自己的前端圖表 panel。
圖表配置
在時序圖表配置場景下,我們需要核心關注配置的有: 1. promQL: 查詢語句 2. Legend: 格式化圖例文本 3. step/interval: 采集點間隔,每隔一段時間,采集一次數據。
一條曲線的數據點數量 = 圖表時長 / 采樣間隔。例如查看最近24小時的數據,采樣 間隔5min,數據點數量=24*60/5=288。
采集間隔時間越短,采樣率越大,圖表數據量越大,曲線越平滑。 采集間隔默認自動計算生成,也可以自定義配置。 4. metric time range: 每個點的數據統計時間區間時長。
以QPS為例,圖表上每個時間點的數據的意義是:在這時間點上,過去n秒間的訪問量。
從上圖可以看到, - 如果采樣間隔 > 統計區間時長: 數據采樣率 < 100%。未能采集到的數據丟棄,不會再圖表上展示。采樣率過小可能會錯誤異常的數據指標。。 - 如果采樣間隔 == 統計區間時長,采樣率100%。 - 如果采樣間隔 < 統計區間時長,數據被重復統計,意義不大。
自定義變量
為了實現一些常用的篩選過濾場景,grafana 提供了變量功能
- 變量配置:變量配置有多種方式(Type),可以自定義選項,也可以根據prometheus 指標的 label 動態拉取。
- 變量使用:變量通過 $xxx 形式去引用。
告警
除了 Prometheus 本身可以配置告警表達式之外:
grafana 也可以配置告警:
數據源
Prometheus 通常用于后端應用的指標數據實時上報,主要用于異常告警,問題排查,所以數據存在時效性,我們不會關注幾個月前的一個已經被排查并 fixed 的指標異常波動告警。
但是,要是我們將 Prometheus 用于業務指標監控,那么我們可能會關注更久遠的數據。
例如我們可能想要看過去一個季度的環比同比增長,用 Prometheus 作為數據源就不合適,因為 Prometheus 是時序數據庫,更多關注實時數據,數據量大,當前數據保存的時效設定只有 3 個月。
那么這個時候可能我們要維護一個長期的統計數據,可能就需要存儲在 mysql 或者其他存儲方式。
grafana 不是 Prometheus 的專屬產品,還支持多種數據源,包括但不限于: - 常見數據庫 - MySql - SQL Server - PostgreSQL - Oracle - 日志、文檔數據庫 - Loki - Elasticsearch - 時序數據庫 - Prometheus - graphite - openTSDB - InfluxDB - 鏈路追蹤 - Jaeger - Zipkin - ....
如果沒有自己需要的數據源配置,還可以安裝 REST API Datasource Plugin, 通過 http 接口查詢作為數據源
總結
了解 grafana 的高度可配置性設計后,有值得思考的幾點: - 關注其設計思想,如果要自己實現一個類似的可視化的 web app,自己會怎么設計? - 自己要做一個高度可配置化的功能,又應該怎么設計? - 深入到業務,例如我們常用的 admin 管理 系統,一些常用的業務功能是否可以高度可配置化?業務強關聯的如何做到配置與業務的有機結合?
等等這些,其實都是值得我們去思考的。
此外,Prometheus 和 grafana 都有些進階的玩法,大家有興趣也可以去探索下。
參考文章
本文首發于 github 博客
如文章對你有幫助,你的 star 是對我最大的支持
其他文章:
掌握甩鍋技術: Typescript 運行時數據校驗
還可以這么玩?超實用 Typescript > 內置類型與自定義類型
typescript 中的 interface 和 type 到底有什么區別?
總結
以上是生活随笔為你收集整理的node.js request get 请求怎么拿到返回的数据_NodeJS运维: 从 0 开始 Prometheus + Grafana 业务性能指标监控...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python 立方体切割块数_blend
- 下一篇: 双代号网络图基础算法_9个简单数学算法在