企業級別Redis監控,細化到每個項目實例

享學課堂特邀作者:老顧
轉載請聲明出處,謝謝!!!

前言

文章中還提到了幾點問題:

  • 如何區分業務
  • 如何發現異常
  • 如何截斷異常
  • 如何優雅擴容

今天老顧這篇文章就重點介紹如何發現異常,爲什麼從這個點去講,因爲這個點是前期的基礎,只有具備發現一些問題,才能夠有後續行爲。

redis監控

在我們現在公司redis監控中,是對整個redis集羣進行監控,如:內存使用情況,客戶端連接數,有多少keys等從整體集羣緯度上面去監控redis的使用情況。當然這些監控也是非常重要的。

現在常規的方案就是利用Prometheus採集數據,並用Grafana進行展示,如下圖

可以這些只是個從整體緯度上面的監控,因爲我們企業很多項目都在用一個公共的redis集羣,那如何監控在每個項目,以及每個項目實例的對公共redis集羣的請求情況呢?

公共Redis集羣監控需求

我們先看下面的圖:

現在公司很多都是微服務架構了,會涉及到會有很多服務實例了。上圖中就是多個項目,多個服務實例在請求公用redis集羣。

這種情況下,我們需要一個底線,就是一定要保證我們的公共redis集羣是穩定的,一旦不穩定會導致我們所有的項目服務不穩定

但是這種架構會出現研發風險,因爲在真實的開發過程中,我們是沒法保證我們所有的開發人員不會出錯,萬一有個開發人員,在寫一些業務時,用到了redis組件,寫了個死循環導致了我們redis請求猛增,導致redis不穩定。或者對redis進行濫用,什麼業務都放到redis中,導致內存崩潰。在這種情況下,我們都沒法知道是哪個項目,哪個實例導致的。我們的運維會處在排查問題的混亂之中。那我們能不能提前設防呢?

我們整理一下需求:

1)我們是否可以提前分配某個項目佔用redis內存的大小,指定每個實例的單位時間最大請求數,最大容忍的異常數。

2)實時監控項目佔用的redis內存情況

3)實時監控項目中每個實例的請求情況

4)一旦項目佔用redis內存大於分配的,就報警;甚至可以自動讓這個項目進入黑名單

5)一旦項目中的實例的請求數,異常數,大於分配的,就報警;甚至可以自動讓這個項目進入黑名單

解決方案

既然需求過來,那我們作爲開發人員,就要項目如何設計我們的方案。因爲需求還是比較多的,我們來梳理一下這些需求業務流程:

項目分配規則其實是比較簡單的,只要設計一下mysql,以及開發一個web服務,在界面進行操作;就是一些基本的CRUD。這邊老顧就不講了。

監控是這篇文章的重點,後面涉及到的監控後的策略,老顧放到後續文章裏面介紹。

監控項目內存使用情況

如果要監控整個redis的內存使用情況,是比較簡單的,上面就介紹了Prometheus+Grafana的方案,當然還可以配合alert報警策略做一些後續流程

基本原理就是利用redis自身的info命令,以及info stats等命令輸出了一些redis服務性能指標

那我們如何細化到監控每個項目的內存佔用情況呢?

小夥伴們是否還記得redis有個持久化的概念,會把一些緩存數據持久化到dump.rdb中,這個文件就存儲了每個key和value的值,以及slot的佔用情況。不過這個文件是二進制的,小夥伴們不能直接打開。

我們的方案就是來解析dump.rdb文件,分析項目內存的使用情況,因爲我們會規定每個項目都會有唯一的項目名稱,會對key有個固定的格式【項目名稱:key名稱】

projectName1:userprojectName2:order

即每個key前面都會帶上項目名稱。這樣有利於我們在解析dump.rdb後,通過項目名稱前綴進行統計項目使用的內存大小。

原理小夥伴們應該知道了吧,但是怎麼去解析dump.rdb文件呢,難度太大了吧;還好有開源社區的貢獻,我們不需要重複造輪子。老顧採用了go語言解析dump.rdb的工具。

git地址爲:

https://github.com/sripathikrishnan/redis-rdb-tools https://gitee.com/gujiachun/rdr(這個是老顧用的,上面的工具也可以考慮,功能更多)

那dump.rdb文件怎麼獲取到呢?redis配置文件中有對應的配置,當然我們解析dump.rdb這個操作要在redis從節點做,相對安全可靠,也可以覆蓋所有業務的 Redis 集羣。

整體流程如下:

這個開源的工具需要一些go語言相關的知識,當然老顧已經打包了一個bin,可以直接用

執行命令

./macos_start dump dump.rdb >> 111.txt

我們可以看到導出來的數據,key表示設置的可以,Bytes就是這個key存儲的value的佔用的字節數,Type表示這對key-value的類型。

我們可以設置定時任務去執行dump操作,當然如果小夥伴會go語言的,可以在這個項目中自行添加業務。這樣我們就可以獲取到所有key佔用內存的情況,再通過分析key的項目前綴,這樣就可以算出來項目佔用內存的情況。

小夥伴們會問,dump.rdb文件很大,那解析需要要多久啊。老顧嘗試了一下,80幾萬的key,dump文件300M左右,用這個工具分析只要 5秒;還是蠻強大的,Go語言實現

實例請求監控

上面我們已經解決了項目內存的佔用情況,這邊我們在解決一下如何監控每個服務實例的請求情況,需要知道請求數,成功數,異常數,最大耗時,最小耗時,平均耗時指標

這塊是參考了限流降級框架Sentinel的源代碼中,實時統計請求指標。

Sentinel是以Bucket(桶)爲單位記錄一段時間內的請求總數、異常總數、總耗時的,而一個Bucket可以是記錄一秒內的數據,也可以是10毫秒內的數據,我們稱這個時間區間爲Bucket的統計單位,是由使用者自定義的:

Bucket存儲一段時間內的請求數、異常數等這些數據用的是一個LongAdder數組,LongAdder保證了數據修改的原子性,數組的每個元素分別代表一段時間內的請求總數、異常數、總耗時。

用枚舉類型MetricEvent的ordinal作爲下標。LongAdder被我替換爲j.u.c包下的atomic類了。

當需要獲取Bucket記錄的總的成功請求數、或者異常總數、或者總的請求處理耗時時,可以通過MetricEvent從LongAdder數組中獲取對應的LongAdder,調用sum方法。

當需要往Bucket添加1個請求、或者一個異常,或者處理請求的耗時時,可以通過MetricEvent從LongAdder數組中獲取對應的LongAdder,調用add方法。

有了Bucket之後,假設我們需要讓Bucket存儲一秒鐘的數據,這樣我們就能夠知道每秒處理成功的請求數(成功QPS)、每秒處理失敗的請求數(失敗QPS),以及處理每個成功請求的平均耗時(avg RT)。但是我們如何才能確保Bucket存儲的就是精確到1秒的數據呢?最low的做法就是啓一個定時任務每秒創建一個Bucket,但統計出來的數據誤差絕對很大。

而Sentinel是這樣實現的。它定義一個Bucket數組,根據時間戳來定位到數組下標。假設我們需要統計每1秒處理的請求數等數據,且只需要保存最近一分鐘的數據。那麼Bucket數組的大小就可以設置爲60,每個Bucket的windowLengthInMs窗口大小就是1000毫秒(1秒)。

由於每個Bucket存儲的是1秒的數據,那麼就可以將當前時間戳去掉毫秒部分,就能得到當前的秒數,假設Bucket數組的大小是無限大的,那麼得到的秒數就是當前要獲取的Bucket所在的數組下標。

但我們不能無限的存儲Bucket,一秒一個Bucket得要多大的內存才能存一天的數據。所以,當我們只需要保留一分鐘的數據時,Bucket數組的大小就是60,將得到的秒數與數組長度取餘數,就得到當前Bucket所在的數組下標。這個數組是循環使用的,永遠只保存最近1分鐘的數據。

取餘數就是循環利用數組。如果想要獲取連續的一分鐘的Bucket數據,就不能簡單的從頭開始遍歷數組,而是指定一個開始時間和結束時間, 從開始時間戳開始計算Bucket存放的數組下標,然後循環每次將開始時間戳加上1秒,直到開始時間等於結束時間。

整體原理如上,還是比較撓的,直接上案例代碼,看效果如何。

請求案例

上面是業務中加入監控埋點,下面我們直接輸出

小夥伴們看到了FlowHelper這個類,就在老顧的開源項目中;

git地址:https://gitee.com/gujiachun/rb-qps-helper

通過這樣的方式,我們就可以改造我們的redis請求client方法了,進行監控埋點就行了。

現在還剩下一個問題,就是我們監控到的數據如何給監控平臺?

如何上報監控數據

一開始老顧想着用定時任務的方式,提交給監控平臺;最後想了想,那監控平臺地址是什麼呢?太耦合了。然後想到了我們spring boot中就有一些監控指標。

通過訪問http://localhost:1100/actuator/metrics就可以看到監控的指標,再通過http://localhost:1100/actuator/prometheus直接可以看到指標數據。

我們也是可以參考一下的,我們每個實例只要暴露出來,讓prometheus自己來拿,而且又是標準流程。具體實現:

引入依賴

實現MeterBinder接口

在利用Gauge 監控類實現就ok了

這樣就可以把我們redis實例的請求指標,展現在http://localhost:1100/actuator/prometheus中了

是不是很酷啊?

總結

這篇文章的知識點很多,想要完成我們文章開頭的需求,需要這些知識一起組合起來,就能夠實現監控的目標了;當然我們需要搭建一個DashBoard界面查看監控數據

我們看每個項目的內存佔比請求,以及有沒有達到閥值,以及對哪些Key佔用比較多的內存,進行排名。也可以監控到每個實例的請求情況,請求數排名,異常數排名等。

可以極大的幫助我們運維和開發人員更多的瞭解我們的redis使用情況。文章裏面只是介紹了核心思想。老顧已經全部實現了,小夥伴們需要源碼,可以聯繫老顧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章