前言
業務監控對許許多多的場景都是十分有意義,業務監控看板可以讓我們比較直觀的看到當前業務的實時情況,然後運營人員可以根據這些情況及時對業務進行調整操作,避免業務出現大問題。
老黃曾經遇到過一次比較尷尬的“事故”。
其中一條業務線,服務著的其中一個商家,把大部分流量切到另外一個地方去了,而我們的運營人員在當天卻是完全不知情,第二天看了昨天的統計報表之後才發現這個商家的量少了很多,才能跟進協調處理。
ps: 當時實時報表比較欠缺,都是第二天凌晨生成昨天的資料包表,也沒有告警機制。
後面就弄了個大螢幕做了業務監控的實時看板,看一眼就知道有什麼風吹草動了。
先來看一下最終的效果圖。
這個圖裡面主要包含了下面幾個內容。
- 總的訂單數量
- 退單的數量
- 建立訂單的頻率
- 不同渠道的訂單量
- 不同渠道的退單量
下面就介紹一下如何實現這樣的業務監控。
搭建基礎設施
這裡涉及的基礎設施就有兩個,一個是prometheus,另一個是grafana。
先啟動prometheus,這裡直接用docker啟動。
$base = Split-Path -Parent $MyInvocation.MyCommand.Definition
$prometheusyml = Join-Path $base prometheus.yml
$fileconfig = Join-Path $base "config"
write-host $prometheusyml
write-host $fileconfig
docker run `
--name prom `
-p 9090:9090 `
-v ${prometheusyml}:/etc/prometheus/prometheus.yml `
-v ${fileconfig}:/etc/prometheus/fileconfig `
prom/prometheus:v2.20.1
下面是prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
alerting:
alertmanagers:
- static_configs:
- targets:
# - alertmanager:9093
rule_files:
scrape_configs:
- job_name: 'file_ds'
file_sd_configs:
- refresh_interval: 10s
files:
- ./fileconfig/*.yml
這裡用了基於檔案的發現機制,沒有用靜態的。更多其他方式,參見 https://prometheus.io/docs/prometheus/latest/configuration/configuration/#scrape_config
這個時候prometheus已經是執行起來了。
再來就是grafana了,啟動這個更加簡單。
docker run -d --name grafana -p 3000:3000 grafana/grafana:7.1.3
執行完,訪問 localhost:3000 就可以看到登入介面了。
確定業務指標(metrics)
確定指標可以說是整個業務監控中最最最最最為主要的一個環節了,只有明確了我們要監控什麼,我們才可以在業務上去進行埋點,拿到想要的資料。
這個其實和我們平時面對的需求是一個樣的,需求明確了,做出來的東西才可能是我們想要的,需求不明確,做出來的東西可能就不會是我們想要的了。
為了幫助大家簡單的理解相關的內容,這裡舉個監控的例子,監控不同渠道的下單和退單量。
涉及到量的,在一天內基本上是屬於只增不減的,這個時候我們一般會選用 counter 型別來處理。
一個是下單,一個是退單,所以這裡定義兩個
- yyyorder_created_total
- yyyorder_canceled_total
counter型別的,一般在命名的時候最好都用_total作為結尾。
還有不同渠道呢?
渠道我們就用 lable 來標識一下。
最後展現格式大致如下:
yyyorder_created_total{appkey="mt",opreator="cw"} 1
yyyorder_canceled_total{appkey="pdd",opreator="cw"} 2
這裡也要注意一個問題,確定指標的時候,也要避免定義太多指標出來,如果可以,考慮用label去進行區分同性質的內容。
業務埋點
在明確了業務指標之後,就要在對應的業務上去進行埋點操作,會對業務程式碼有一定的侵入性,當然如果業務程式碼寫得足夠好,耦合的東西少,或許可以藉助AOP來埋點,從而降低侵入性。
後面就寫個簡單的例子來模擬業務埋點這一塊。
建立一個ASP.NET Core的專案,並安裝prometheus-net.AspNetCore這個nuget包。
<ItemGroup>
<PackageReference Include="prometheus-net.AspNetCore" Version="3.6.0" />
</ItemGroup>
其次是啟用 ASP.NET Core exporter middleware
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
// 這一句。
endpoints.MapMetrics();
endpoints.MapControllers();
});
}
最後就是埋點了。
[ApiController]
[Route("")]
public class HomeController : ControllerBase
{
private static readonly Counter OrderCreatedCount = Metrics
.CreateCounter("yyyorder_created_total", "Number of created orders.", new CounterConfiguration
{
LabelNames= new [] { "appkey", "opreator" }
});
private static readonly Counter OrderCanceledCount = Metrics
.CreateCounter("yyyorder_canceled_total", "Number of canceled orders.", new CounterConfiguration
{
LabelNames = new[] { "appkey", "opreator" }
});
[HttpGet]
public string Get()
{
var appKeys = new[] { "ali", "pdd", "mt" };
var opreators = new[] { "cw", "pz" };
var rd = new Random((int)DateTimeOffset.Now.ToUnixTimeMilliseconds()).Next(0, 2000);
var appKeyidx = rd % 3;
var opreatoidx = rd % 2;
OrderCreatedCount.WithLabels(appKeys[appKeyidx], opreators[opreatoidx]).Inc();
var cRd = new Random((int)DateTimeOffset.Now.ToUnixTimeMilliseconds()).NextDouble();
if (cRd < 0.3d)
{
OrderCanceledCount.WithLabels(appKeys[appKeyidx], opreators[opreatoidx]).Inc();
}
return "ok";
}
}
上面這個控制器中,建立了兩個Counter,就是上面確定業務指標中定義好的。
這裡是每訪問一次,就建立一個訂單,同時生成一個隨機數,如果是小於0.3,那麼就當它是退單的,這樣就可以把兩種指標都模擬出來了。
程式剛啟動是有部分預設指標的。
當我們訪問埋點的地址後,可以發現我們自定義的業務指標也已經有資料了。
到這裡,資料已經有了,我們要怎麼呈現呢?
要想呈現資料,我們需要先讓prometheus來儲存我們的業務指標資料。
資料寫入
把資料寫入prometheus有兩鍾方式,一種是pull,一種是push。
pull是讓prometheus主動去拉取我們產生的資料,只要我們暴露一個地址出來即可,這中也是比較推薦的做法。
push方式要藉助pushgateway,埋點資料要先主動推送到pushgateway,後面在由pushgateway把資料寫進prometheus。
預設情況下,當我們用了endpoints.MapControllers();
之後,就會把資料暴露在 http://ip:port/metrics 這個地址上。
知道要用pull的方式後,還要做什麼呢?當然就是要去配置promethues了。
前面我們的 scrape_configs
是通過檔案去自動發現的,所以只要在掛載的路徑上面加一個對應的yml檔案就可以了。
老黃這裡加了一個nc-service.yml
,具體內容如下:
- labels:
service: nc
project: demo
targets:
- 192.168.1.103:9874
- 192.168.1.103:9875
這個時候就可以在Targets裡面看到我們這兩個地址的資訊了。
通過prometheus的預設介面,也可以發現資料已經正常讀取了。
後面就是真正的資料查詢和展示了。
資料展示
通過上面的步驟,我們已經保證資料可以正常寫入和查詢了,現在就在grafana中建立一個業務監控看板了。
在grafana中先配置我們的資料來源。
這裡填上我們prometheuse的地址儲存就可以了,可以看到那個綠色的提示,告訴我們這個資料來源是正常工作的了。
先來一個總的訂單數。
建立一個新的dashboard,再建立一個Panel。
我們在panel中填寫我們的資訊還有就是選擇要的圖形。
然後就是寫上查詢條件,就可以看到我們要的結果了。
訂單總數這個查詢如下:
sum(ceil(increase(yyyorder_created_total[1d])))
裡面用到了, sum、ceil、increase這三個函式。
其中 increase 是用來統計一段時間範圍內的增量。後面帶了 [1d] 這個範圍表明這裡是檢視1天內的增量。
ceil是用來把increase的結果進行四捨五入的,可能有人會好奇,怎麼還會要四捨五入呢?
看看下面這個圖,大家就會發現,非常多的小數點。
其實這個和prometheus的統計方法是有關係的,這裡不展開,先這樣用著。
sum 這個是用來求和的,指標中還包含了很多label,我們還要把每個label的求和,才是真正的結果。
所以這裡就得到了下面這個結果。
退單總數的查詢和訂單總數一樣,只是把名字換成退單的即可。
sum(ceil(increase(yyyorder_canceled_total[1d])))
再來看看各渠道的訂單統計。
既然是看各渠道的統計,那麼這裡就要用到前面定義好的label了。appkey代表的就是渠道,那麼我們基於它去分一下組就可以了。
就得到下面的查詢。
sum by (appkey) (ceil(increase (yyyorder_created_total[1d])))
結果如下:
同理,各渠道退單的也是一樣的寫法
sum by (appkey) (ceil(increase (yyyorder_canceled_total[1d])))
ps: 如果想把訂單和退單的放在一個圖裡面,可以加多個查詢。
示例如下:
現在有了所有渠道的總量,各個渠道獨立的總量,那麼我們有辦法知道某個時間段的趨勢嗎?
這個是肯定有的,且聽老黃慢慢道來。
有上面這個疑問,多半是經歷過,某個時間段量非常多,但是有的時間段又幾乎為零,玩的就是心跳呀。
我們可以把這個稱之為時間段內的訂單增長情況。
這裡需要用到rate函式,這個就是幫助我們統計增長速率的函式。
它統計的是每秒的平均增長率,這個粒度有點太細,所以我們會在這個基礎上乘以60,放大到一分鐘。
然後在看它sum的結果,最後才四捨五入。
ceil(sum(rate(yyyorder_created_total[5m]) * 60))
結果如下:
從這個結果可以看出,訂單大部分時間都沒有增長,只在中間那個時間段,有部分單進來。
到這裡主要的各個小panel已經完成了,剩下的就是在dashboard裡面調整位置,大小那些了。
總結
這樣打造出來的監控看板還是挺不錯的,不過還是要注意下面幾個問題的
- prometheus是把資料儲存在本地的,總是會達到上限的,這裡要麼是定期刪,要麼是寫到遠端儲存。
- prometheus自己獨立的查詢語法可能剛開始會比較不適應,查不出自己想要的結果,這裡多查查資料,多實踐基本問題也不大。
- 業務埋點這一個塊,還是要儘可能的減少對現有業務程式碼的侵入性。
- 業務指標一定要確定好,不然埋點痛苦,查詢也痛苦。
這裡還沒有涉及到告警相關的內容,後面有時間再寫一個告警相關的。
文中示例程式碼:
https://github.com/catcherwong-archive/2020/tree/master/08/PromDemo
本文首發於我的個人公眾號 不才老黃 ,不定期釋出一些內容,有興趣的可以關注一下喲!