基于?Prometheus、InfluxDB?與?Grafana?打造監(jiān)控平臺
時間:2021-11-12 13:56:46
手機看文章
掃描二維碼
隨時隨地手機看文章
[導讀]在本文中,我將把幾個常用的監(jiān)控部分給梳理一下。前面我們提到過,在性能監(jiān)控圖譜中,有操作系統(tǒng)、應用服務器、中間件、隊列、緩存、數(shù)據(jù)庫、網(wǎng)絡、前端、負載均衡、Web服務器、存儲、代碼等很多需要監(jiān)控的點。顯然這些監(jiān)控點不能在一個專欄中全部覆蓋并一一細化,我只能找最常用的幾個,做些邏輯思...

在本文中,我將把幾個常用的監(jiān)控部分給梳理一下。前面我們提到過,在性能監(jiān)控圖譜中,有操作系統(tǒng)、應用服務器、中間件、隊列、緩存、數(shù)據(jù)庫、網(wǎng)絡、前端、負載均衡、Web 服務器、存儲、代碼等很多需要監(jiān)控的點。顯然這些監(jiān)控點不能在一個專欄中全部覆蓋并一一細化,我只能找最常用的幾個,做些邏輯思路的說明,同時也把具體的實現(xiàn)描述出來。如果你遇到了其他的組件,也需要一一實現(xiàn)這些監(jiān)控。
在本篇中,主要想說明白下圖的這個監(jiān)控邏輯。

這應該是現(xiàn)在最流行的一套監(jiān)控邏輯了吧。我今天把常見的使用 Grafana、Prometheus、InfluxDB、Exporters 的數(shù)據(jù)展示方式說一下,如果你剛進入性能測試領域,也能有一個感性的認識。
有測試工具,有監(jiān)控工具,才能做后續(xù)的性能分析和瓶頸定位,所以有必要把這些工具的邏輯跟你擺一擺。
所有做性能的人都應該知道一點,不管數(shù)據(jù)以什么樣的形式展示,最要緊的還是看數(shù)據(jù)的來源和含義,以便做出正確的判斷。
我先說明一下 JMeter 和 node_exporter 到 Grafana 的數(shù)據(jù)展示邏輯。至于其他的 Exporter,我就不再解釋這個邏輯了,只說監(jiān)控分析的部分。
JMeter InfluxDB Grafana 的數(shù)據(jù)展示邏輯

一般情況下,我們用 JMeter 做壓力測試時,都是使用 JMeter 的控制臺來查看結(jié)果。如下圖所示:

或者裝個插件來看結(jié)果:

或者用 JMeter 來生成 HTML:

這樣看都沒有問題,我們在前面也強調(diào)過,對于壓力工具來說,我們最多只關(guān)心三條曲線的數(shù)據(jù):TPS(T 由測試目標定義)、響應時間、錯誤率。這里的錯誤率還只是輔助排查問題的曲線,沒有問題時,只看 TPS 和響應時間即可。不過采取以上三種方式有幾個方面的問題。
- 整理結(jié)果時比較浪費時間。
- 在 GUI 用插件看曲線,做高并發(fā)時并不現(xiàn)實。
- 在場景運行時間比較長的時候,采用生成 HTML 的方式,會出現(xiàn)消耗內(nèi)存過大的情況,而實際上,在生成的結(jié)果圖中,有很多生成的圖我們并不是那么關(guān)注。
- 生成的結(jié)果保存之后再查看比較麻煩,還要一個個去找。
那么如何解決這幾個問題呢?
用 JMeter 的 Backend Listener 幫我們實時發(fā)送數(shù)據(jù)到 InfluxDB 或 Graphite 可以解決這樣的問題。
Graphite Backend Listener 的支持是在 JMeter 2.13 版本,InfluxdDB Backend Listener 的支持是在 JMeter 3.3 的版本,它們都是用異步的方式把數(shù)據(jù)發(fā)送出來,以便查看。
其實有這個 JMeter 發(fā)送給 InfluxDB 的數(shù)據(jù)之后,我們不需要看上面的那些 HTML 數(shù)據(jù),也可以直觀地看到系統(tǒng)性能的性能趨勢。
并且這樣保存下來的數(shù)據(jù),在測試結(jié)束后想再次查看也比較方便比對。
JMeter InfluxDB Grafana 的結(jié)構(gòu)如下:

在這個結(jié)構(gòu)中,JMeter 發(fā)送壓力到服務器的同時,統(tǒng)計下 TPS、響應時間、線程數(shù)、錯誤率等信息。默認每 30 秒在控制臺輸出一次結(jié)果(在 jmeter.properties 中有一個參數(shù) #summariser.interval=30 可以控制)。
配置了 Backend Listener 之后,將統(tǒng)計出的結(jié)果異步發(fā)送到 InfluxDB 中。最后在 Grafana 中配置 InfluxDB 數(shù)據(jù)源和 JMeter 顯示模板。
然后就可以實時查看 JMeter 的測試結(jié)果了,這里看到的數(shù)據(jù)和控制臺的數(shù)據(jù)是一樣。
但如果這么簡單就說完了,這篇文章也就沒價值了。下面我們來說一下,數(shù)據(jù)的傳輸和展示邏輯。
JMeter 中 Backend Listener 的配置

下面我們就 InfluxDB 的 Backend Listener 做個說明。它的配置比較簡單,在腳本中加上即可。

我們先配置好 InfluxDB URL、Application 等信息,Application 這個配置可以看成是場景名。
那么 JMeter 如何將數(shù)據(jù)發(fā)給 InfluxDB 呢?請看源碼中的關(guān)鍵代碼,如下所示:private?void?addMetrics(String?transaction,?SamplerMetric?metric)?{
????????//?FOR?ALL?STATUS
????????addMetric(transaction,?metric.getTotal(),?metric.getSentBytes(),?metric.getReceivedBytes(),?TAG_ALL,?metric.getAllMean(),?metric.getAllMinTime(),
????????????????metric.getAllMaxTime(),?allPercentiles.values(),?metric::getAllPercentile);
????????//?FOR?OK?STATUS
????????addMetric(transaction,?metric.getSuccesses(),?null,?null,?TAG_OK,?metric.getOkMean(),?metric.getOkMinTime(),
????????????????metric.getOkMaxTime(),?okPercentiles.values(),?metric::getOkPercentile);
????????//?FOR?KO?STATUS
????????addMetric(transaction,?metric.getFailures(),?null,?null,?TAG_KO,?metric.getKoMean(),?metric.getKoMinTime(),
????????????????metric.getKoMaxTime(),?koPercentiles.values(),?metric::getKoPercentile);
????????metric.getErrors().forEach((error,?count)?->?addErrorMetric(transaction,?error.getResponseCode(),
????????????????????error.getResponseMessage(),?count));
????}從這段代碼可以看出,站在全局統(tǒng)計的視角來看,這里把 JMeter 運行的統(tǒng)計結(jié)果,比如事務的 Total 請求、發(fā)送接收字節(jié)、平均值、最大值、最小值等,都加到 metric 中,同時也會把成功和失敗的事務信息添加到 metric 中去。
在源碼中,還有更多的添加 metric 的步驟,你有興趣的話,也可以看一下 JMeter 源碼中的InfluxdbBackendListenerClient.java。
保存了 metric 之后,再使用 InfluxdbMetricsSender 發(fā)送到 InfluxDB 中去。發(fā)送關(guān)鍵代碼如下:@Override?public?void?writeAndSendMetrics()?{
?........?if?(!copyMetrics.isEmpty())?{?try?{?if(httpRequest?==?null)?{
????????????????????httpRequest?=?createRequest(url);
????????????????}
????????????????StringBuilder?sb?=?new?StringBuilder(copyMetrics.size()*35);?for?(MetricTuple?metric?:?copyMetrics)?{?//?Add?TimeStamp?in?nanosecond?from?epoch?(?default?in?InfluxDB?)
?sb.append(metric.measurement)
????????????????????????.append(metric.tag)
????????????????????????.append("?")?//$NON-NLS-1$
?.append(metric.field)
????????????????????????.append("?")
????????????????????????.append(metric.timestamp "000000")?
????????????????????????.append("\n");?//$NON-NLS-1$
?}
????????????????StringEntity?entity?=?new?StringEntity(sb.toString(),?StandardCharsets.UTF_8);
????????????????
????????????????httpRequest.setEntity(entity);
????????????????lastRequest?=?httpClient.execute(httpRequest,?new?FutureCallback
????????????????????@Override?public?void?completed(final?HttpResponse?response)?{?int?code?=?response.getStatusLine().getStatusCode();?/*?*?HTTP?response?summary?2xx:?If?your?write?request?received
?????????????????????????*?HTTP?204?No?Content,?it?was?a?success!?4xx:?InfluxDB
?????????????????????????*?could?not?understand?the?request.?5xx:?The?system?is
?????????????????????????*?overloaded?or?significantly?impaired.?*/
????????????????????????if?(MetricUtils.isSuccessCode(code))?{?if(log.isDebugEnabled())?{
????????????????????????????????log.debug("Success,?number?of?metrics?written:?{}",?copyMetrics.size());
????????????????????????????}?
????????????????????????}?else?{
????????????????????????????log.error("Error?writing?metrics?to?influxDB?Url:?{},?responseCode:?{},?responseBody:?{}",?url,?code,?getBody(response));
????????????????????????}
????????????????????}
????????????????????@Override?public?void?failed(final?Exception?ex)?{
????????????????????????log.error("failed?to?send?data?to?influxDB?server?:?{}",?ex.getMessage());
????????????????????}
????????????????????@Override?public?void?cancelled()?{
????????????????????????log.warn("Request?to?influxDB?server?was?cancelled");
????????????????????}
????????????????});???????????????
?........
????????????}
????????}
????}通過 writeAndSendMetrics,就將所有保存的 metrics 都發(fā)給了 InfluxDB。
InfluxDB 中的存儲結(jié)構(gòu)

然后我們再來看下 InfluxDB 中如何存儲:>?show?databases??
name:?databases??
name??
----??
_internal??
jmeter??
>?use?jmeter??
Using?database?jmeter??
>??
>?show?MEASUREMENTS??
name:?measurements??
name??
----??
events??
jmeter??
>?select?*?from?events?where?application='7ddemo'??
name:?events??
time?application?text?title??
----?-----------?----?-----??
1575255462806000000?7ddemo?Test?Cycle1?started?ApacheJMeter??
1575256463820000000?7ddemo?Test?Cycle1?ended?ApacheJMeter??
..............??
n>?select?*?from?jmeter?where?application='7ddemo'?limit?10??
name:?jmeter??
time?application?avg?count?countError?endedT?hit?max?maxAT?meanAT?min?minAT?pct90.0?pct95.0?pct99.0?rb?responseCode?responseMessage?sb?startedT?statut?transaction??
----?-----------?---?-----?----------?------?---?---?-----?------?---?-----?-------?-------?-------?--?------------?---------------?--?--------?------?-----------??
1575255462821000000?7ddemo?0?0?0?0?0?internal??
1575255467818000000?7ddemo?232.82352941176472?17?0?17?849?122?384.9999999999996?849?849?0?0?all?all??
1575255467824000000?7ddemo?232.82352941176472?17?849?122?384.9999999999996?849?849?0?0?all?0_openIndexPage??
1575255467826000000?7ddemo?232.82352941176472?17?849?122?384.9999999999996?849?849?ok?0_openIndexPage??
1575255467829000000?7ddemo?0?1?1?1?1?internal??
1575255472811000000?7ddemo?205.4418604651163?26?0?26?849?122?252.6?271.4?849?0?0?all?all??
1575255472812000000?7ddemo?0?1?1?1?1?internal??
1575255472812000000?7ddemo?205.4418604651163?26?849?122?252.6?271.4?849?ok?0_openIndexPage??
1575255472812000000?7ddemo?205.4418604651163?26?849?122?252.6?271.4?849?0?0?all?0_openIndexPage??
1575255477811000000?7ddemo?198.2142857142857?27?0?27?849?117?263.79999999999995?292.3500000000001?849?0?0?all?all這段代碼也就是說,在 InfluxDB 中,創(chuàng)建了兩個 MEASUREMENTS,分別是 events 和 jmeter。這兩個各自存了數(shù)據(jù),我們在界面中配置的 testtile 和 eventTags 放在了 events 這個 MEASUREMENTS 中。在模板中這兩個值暫時都是不用的。
在 JMeter 這個 MEASUREMENTS 中,我們可以看到 application 和事務的統(tǒng)計信息,這些值和控制臺一致。在 Grafana 中顯示的時候,就是從這個表中取出的數(shù)據(jù),根據(jù)時序做的曲線。
Grafana 中的配置

有了 JMeter 發(fā)送到 InfluxDB 中的數(shù)據(jù),下面就來配置一下 Grafana 中的展示。首先,要配置一個 InfluxDB 數(shù)據(jù)源。如下所示:

在這里配置好 URL、Database、User、Password 之后,直接點擊保存即可。
然后添加一個 JMeter Dashboard,我們常用的 Dashboard 是 Grafana 官方 ID 為 5496 的模板。導入進來后,選擇好對應的數(shù)據(jù)源。

然后就看到界面了。

這時還沒有數(shù)據(jù),我們稍后做個示例,看下 JMeter 中的數(shù)據(jù)怎么和這個界面的數(shù)據(jù)對應起來。我們先看下圖中兩個重要的數(shù)據(jù)查詢語句吧。
TPS 曲線
SELECT?last("count")?/?$send_interval?FROM?"$measurement_name"?WHERE?("transaction"?=~?/^$transaction$/?AND?"statut"?=?'ok')?AND?$timeFilter?GROUP?BY?time($__interval)上面這個就是 Total TPS 了,在這里稱為 throughput。
關(guān)于這個概念,我在第一篇中就已經(jīng)有了說明,這里再次提醒,概念的使用在團隊中要有統(tǒng)一的認識,不要受行業(yè)內(nèi)一些傳統(tǒng)信息的誤導。
這里取的數(shù)據(jù)來自 MEASUREMENTS 中成功狀態(tài)的所有事務。
響應時間曲線:SELECT?mean("pct95.0")?FROM?"$measurement_name"?WHERE?("application"?=~?/^$application$/)?AND?$timeFilter?GROUP?BY?"transaction",?time($__interval)?fill(null)
這里是用 95 pct 內(nèi)的響應時間畫出來的曲線。
整體展示出來的效果如下:

數(shù)據(jù)比對
首先,我們在 JMeter 中配置一個簡單的場景。10 個線程,每個線程迭代 10 次,以及兩個 HTTP 請求。

也就是說,這時會產(chǎn)生 10x10x2=200 次請求。我們用 JMeter 跑起來看一下。

看到了吧,這個請求數(shù)和我們預想的一樣。下面我們看一下 Grafana 中展示出來的結(jié)果。

還有針對每個事務的統(tǒng)計情況。

至此,JMeter 到 Grafana 的展示過程就完成了。以后我們就不用再保存 JMeter 的執(zhí)行結(jié)果了,也不用等著 JMeter 輸出 HTML 了。
node_exporter Prometheus Grafana 的數(shù)據(jù)展示邏輯

對性能測試來說,在常用的 Grafana Prometheus Exporter 的邏輯中,第一步要看的就是操作系統(tǒng)資源了。所以在這一篇中,我們將以 node_exporter 為例來說明一下操作系統(tǒng)抽取數(shù)據(jù)的邏輯,以便知道監(jiān)控數(shù)據(jù)的來源,至于數(shù)據(jù)的含義,我們將在后續(xù)的文章中繼續(xù)描述。
首先,我們還是要畫一個圖。

現(xiàn)在 node_exporter 可以支持很多個操作系統(tǒng)了。官方列表如下:

當然不是說只支持這些,你也可以擴展自己的 Exporter。
配置 node_exporter
node_exporter 目錄如下:[root@7dgroup2?node_exporter-0.18.1.linux-amd64]#?ll??
total?16524??
-rw-r--r--?1?3434?3434?11357?Jun?5?00:50?LICENSE??
-rwxr-xr-x?1?3434?3434?16878582?Jun?5?00:41?node_exporter??
-rw-r--r--?1?3434?3434?463?Jun?5?00:50?NOTICE}啟動:[root@7dgroup2?node_exporter-0.18.1.linux-amd64]#./node_exporter?--web.listen-address=:9200?