限流系統如何發現系統的熱點
限流系統是對資源呼叫的控制組件,主要涵蓋授權、限流、降級、呼叫統計等功能模組。限流系統有兩個基礎概念:資源和策略,對特定的資源採取不同的控制策略,起到保障應用穩定性的作用。限流系統提供了多個預設切入點覆蓋了大部分使用場景,保證對應用的低侵入性;同時也支援硬編碼或者自定義aop的方式來支援特定的使用需求。限流系統提供了全面的執行狀態監控,實時監控資源的呼叫情況(qps、rt、限流降級等資訊)。
如何利用限流系統的特性,來統計熱點呢?在這裡,我們主要介紹一下,限流系統是如何來判斷熱點的,它的工作原理是什麼,它的效能如何;它目前已經在哪些場景裡面使用。
1. 熱點的特性
a) 海量的統計基數
可能的熱點分為兩種,一種是事前已知的(例如營銷人員的已經整理出秒殺商品的列表),另外一種是突發的,不可以預計的(例如被刷單的商品,或者是黑馬產品)。對於前面一種,只需要計算這些已知的列表即可,但是對於後者,由於基數是海量的,舉個例子,如果有一個方法,它傳入的引數是商品id, 這個商品的id以淘寶的規模來說,是上億的。如果把所有的商品id都記錄統計起來,再進行排序,統計出熱點,這對於實時的工具來說,是不可行的。所以為了解決熱點統計,我們必須找到一個好的資料結構,這個結構能夠僅儲存最常常被訪問的一定數量商品id,並且可以控制結構的大小,統計量。這是非常重要的一個步驟。
b) 統計熱點的單位時間的維度。
這裡有一個必須強調的概念: 單位時間而不是總訪問量。 打個比方,一個商品,在一小時以內被訪問了3600次,但是它很均勻的每秒被訪問一次,那麼這個商品可能在系統的承受範圍之內,並會對系統帶來損傷;而另外一個商品,它一小時被訪問了60次,但都落在同一秒,那麼這個商品可能就是一個熱點商品。對於”單位時間”的定義,是決策一個引數是否成為熱點的一個重要的因素;如果太粗,必然會帶來毛刺,導致系統性能不平滑;如果太細,會給效能帶來挑戰。
c) 在分散式系統給統計帶來的挑戰
熱點的統計範圍可能是單機,也可能是叢集。如何能快速的在叢集中統計,並且讓限流規則在單機上生效,是非常重要的。
2. 如何解決上述問題
2.1 首先,找到一個可行的資料結構
這個結構必須滿足訪問修改快速,併發性好,佔用記憶體空間少,儲存的個數大小可控制,並且可以驅逐訪問量少的entry,從而可以達到實時統計熱點的查詢數字。
其實,業界有一個現成的好結構。它就是google團隊用於guava裡的ConcurrentLinkedHashMap ( http://code.google.com/p/concurrentlinkedhashmap )。
它有下面幾個特點:
2.1.a 支援併發。它是實現了ConcurrentMap介面的。基本上它的實現是遵循concurrentMap的思想的。這裡就不多贅述。
2.1.b 它把我們普通concurrenthashmap的segments用一個額外的雙鏈表維護起來,通過操作這個連結串列,裡面的entry可以在O(1)的時間之類刪除或者重新排序。當一個entry被訪問,則被移動到表頭;當這個map的容量超過預設,則移除位於連結串列尾的entry。這樣就可以保證表頭是最新被使用的entry,表尾是最近最少被使用的entry。
2.1.c 從另外一個角度來說,可以把ConcurrentLinkedHashMap 分成兩個view. 一個是同步的,一個是非同步的。Hashtable對於呼叫方來說是同步的,連結串列對於呼叫方式不透明的,非同步的。
2.1.d 效能和concurrenthashmap的比較, 肯定比concurrenthashmap差,但是屬於可以忍受的範圍。下面是官方給出的資料:
綜上所述,我們可以利用這個LRU concurrent map link,保證我們在單位時間內,僅儲存有限數量訪問量較高的key,最近比較少訪問的key,我們則可以認為它不是熱點,當map的Size達到上限之後,清除這個key。
2.2 統計單位時間的維度
現在我們來看第二個問題,如何統計單位時間的key的qps。其實這個正是限流系統的拿手好戲,用動態滑動視窗平滑的統計qps。
簡單的說,限流系統為每個簇點(可以簡單的理解為需要統計的方法),做了一個滑動視窗。更具體的說,即限流系統把取樣視窗長度設為2秒,這個取樣視窗又被分割為20個格子。通過用一定的演算法來統計這20個格子的平均滑動視窗累積平均值來進行決策。
為什麼採用平均滑動視窗累計平均值是為了削平毛刺qps(在某個點突發的qps)對整體的影響。該公式如下:
更具體的說明在:
http://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average 可以查到。
說到這裡,聰明的讀者應該早就猜到了,限流系統通過把兩個利器結合起來,即滑動視窗和google concurrent link hashmap,可以統計在固定的時間,最常常使用的引數的值。
限流系統資料結構如下所示:
具體的程式碼在: http://gitlab.alibaba-inc.com/middleware-asp/限流系統 歡迎大家來指正。
2.3 如何解決叢集的挑戰
2.3.1 幸運的是,限流系統天生自帶獲取簇點qps的功能
使用過限流系統的人都會發現,8719這個埠是被限流系統佔用了的。通過這個埠,我們可以用獲取到簇點的qps統計資訊。有了這個“後門”,我們就可以輕鬆快速的獲取到qps的資訊了。
2.3.2 如何在大叢集裡彙總這些qps的資訊
接下來要解決的問題就是,如何在上千臺機器裡面快速彙總這些資訊。還好之前我們在做2.0.7的叢集統計的時候,有了一定的經驗。 這個演算法後來也用在了預案分配巨大的url task中。簡單的說,就是用一個佇列來放任務,多個執行緒來執行任務,一個執行緒來merge取回的結果。通過使用這個方法,一個比較大的叢集,例如buy等,2秒可以返回統計結果。

有了叢集的總體彙總資訊之後,我們再將這個資訊利用diamond來推廣到具體的機器上去。這樣的延遲大概是2-3s左右。
3 總結以及限流系統性能報告
簡單的說,存放一個這樣的結構,大概大小是8k。它的預設引數是每個簇點的存放2s的滑動視窗,20個格子,每個格子最多可以儲存200個引數,那麼在最壞的情況下,這個結構的大小將會是8k+4000個引數大小。
吞吐量在我的日常機器上是29W,在生產機上應該會更好。
原文釋出時間為:2018-09-29
本文作者:中介軟體小哥
本文來自雲棲社群合作伙伴“ ofollow,noindex">Java架構沉思錄 ”,瞭解相關資訊可以關注“ Java架構沉思錄 ”。