算法實戰(四):剖析微服務接口鑑權限流背後的數據結構和算法

算法實戰(四):剖析微服務接口鑑權限流背後的數據結構和算法

微服務就是把複雜的大應用解耦拆分成幾個小應用,大應用拆分成微服務之後服務之間的調用關係更復雜,服務治理就成了微服務的一個技術重點

服務治理就是管理微服務,保證平臺整體正常,平穩運行

鑑權背景介紹

有一個微服務叫用戶服務,提供很多用戶相關的接口,比如獲取用戶信息、註冊、登錄等,給公司內部的其他應用使用,但並不是所有應用,都可以訪問這個用戶服務,也並不是每個有訪問權限的應用都可以訪問用戶服務的所有接口

用戶服務 -> 1(接口1) A 可訪問接口1,2

​ -> 2(接口2) B 可訪問接口3,4

​ -> 3(接口3) C 可訪問接口1,3

​ -> 4(接口4) D 可訪問接口4

1.查看用戶基本信息 2.查看詳細信息 3.登錄 4.註冊

A,B,C,D都可以訪問用戶信息,但每個應用只能訪問用戶服務的部分接口

要實現接口鑑權功能,我們需要事先將應用對接口的訪問權限規則設置好,當某個應用訪問其中一個接口的時候,可以拿應用的請求URL,在規則中進行匹配,如果成功,允許訪問,如果沒有可以匹配的規則,說明這個應用沒有這個接口的訪問權限,拒絕服務。

如何實現快速鑑權?

不同接口的鑑權實現方式是類似的,這裏是HTTP接口實現,鑑權的原理簡單好理解,該用什麼數據結構來存儲規則呢?用戶請求URL在規則中快速匹配用什麼算法呢?

1 如何實現精確匹配規則?

只有當請求URL跟規則中配置的某個接口精確匹配時,這個請求才能被接受、處理

規則:

APP_ID_A: 請求舉例:

​ /user/info/base APP_ID_A訪問/user/info/base PASS

​ /user/info/detail APP_ID_B訪問/user/login REJECT 拒絕

APP_ID_B: APP_ID_C訪問/user/info/detail REJECT

​ /user/register APP_ID_D訪問/user/register PASS

​ /user/login

APP_ID_C:

​ /user/login

​ /login/info/base

APP_ID_D:

​ /user/register

不同的應用對應不同的規則集合,採用散列表來存儲這種對應關係,每個應用對應的規則集合,如何存儲和匹配,將每個應用對應的權限規則,存儲在一個字符串數組中,當用戶請求來的時候拿用戶的請求URL,在這個字符串數組中逐一匹配,匹配的算法就是字符串匹配算法,爲了加快匹配速度,可以按照字符串的大小給規則排序,把它組織成有序數組,當要查找某個URL能否匹配其中某條規則的時候,採用二分查找算法在有序數組中進行匹配

2 如何實現前綴匹配規則?

只要某條規則可以匹配請求URL的前綴,就說這條規則能夠跟這個請求URL匹配

APP_ID_A:

​ /user/info/base 請求舉例:

​ /user/info/detail APP_ID_A訪問/user/info/base/name PASS

​ /user/register APP_ID_A訪問/user/info/detail/address PASS

​ /user/login

……

不同的應用對應不同的規則集合,採用散列表來存儲對應關係

將每個用戶的規則集合組織成trie樹這種數據結構,Trie樹中的每個節點不是存儲單個字符,而是存儲接口被/分割之後的子目錄,比如"/user/name"被分割成"user" "name"兩個子目錄,所以在Trie樹中可以把每個節點的子節點們,組織成有序數組這種數據結構,在匹配過程中,利用二分查找方法決定從一個節點應該跳到哪一個子節點

3 如何實現模糊匹配規則?

如何規則更加複雜,規則中包含通配符,比如**表示匹配任意多個子目錄,*表示匹配任意一個子目錄,只要用戶請求URL可以跟某條規則模糊匹配,就可以說這條規則適用於這個請求

APP_ID_A:

​ /user/info/*

​ /user/wallet/**/rmb

​ /user/register

​ /user/login

請求舉例:

​ APP_ID_A訪問/user/info/base PASS

​ APP_ID_A訪問/user/info/hello PASS

​ APP_ID_A訪問/user/wallet/prituate/available/rmb PASS

​ APP_ID_A訪問/user/wallet/public/rmb PASS

不同的應用對應不同的規則集合,用散列表存儲這種對應關係,每個用戶對應的規則集合,該用什麼數據結構存儲?針對包含通配符的模糊匹配,該使用什麼算法實現呢?

採用回溯算法,拿請求URL跟每條規則逐一進行模糊匹配,更加優化一下的是,結合實際情況挖掘出一個隱形條件,即並不是每條規則都包含通配符,包含通配符的僅僅是少數,把不包含通配符的規則和包含通配符的規則分開處理,把不包含通配符的規則組織成有序數組或Trie樹(精確匹配,組織成有序數組,是前綴匹配,組織成Trie樹)剩下的是少數包含通配符的規則,只要簡單的存儲在一個數組中即可,雖然匹配起來非常慢

當接收到一個請求URL之後,先在不包含通配符的有序數組或者Trie樹中查找,如果能匹配,就不需要繼續再通配符規則中匹配了,不能匹配,就繼續再通配符規則中查找匹配

限流背景介紹

限流,對接口調用的頻率進行限制,比如每秒鐘不能超過100次調用,超過之後拒絕服務,在秒殺、大促、雙11、618等場景中,限流已經成爲保證系統平穩運行的一種標配技術解決方案,按照不同的限流粒度,限流分爲很多種類型,比如給每個接口限制不同的訪問頻率,或者給所有接口限制總的訪問頻率,又或者更細粒度的限制某個應用對某個接口的訪問頻率等

如何實現精準限流?

限流算法是固定時間窗口限流算法,首先選定一個時間起點,之後每當有接口請求到來,就將計數器加一,如果在當前時間窗口內,根據限流規則(比如每秒鐘最大允許100次訪問請求),出現累加訪問次數超過限流值的情況時,就拒絕後續的訪問請求,當進入下一個時間窗口之後,計數器清零重新計數

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-NDYujKRc-1584348105085)(D:\數學\MobileFile\1584261480529.jpeg)]

基於固定時間窗口的限流算法的缺點是限流策略過於粗略,無法應對兩個時間窗口臨界時間內的突發流量,假設限流規則是每秒鐘不能超過100次接口請求,第一個1s時間窗口內,100次接口請求都集中在最後10ms內,第二個1s時間窗口內,100次接口請求都集中在最開始的10ms內,最燃兩個時間窗口內流量都符合要求,但是兩個時間窗口臨街的20ms中,會集中有200次接口請求,固定時間窗口限流算法並不能對這種情況進行限制,所以這20ms中200次請求有可能壓垮系統

所以限制任意時間窗口(比如1s)內,接口請求數都不能超過某個閾值(比如100次),因此,相對於固定時間限流算法,這個算法叫滑動時間窗口限流算法

流量經過滑動時間窗口限流算法整形之後,保證任意一個1s時間窗口內,都不會超過最大允許的限流值,從流量曲線上看會更加平滑,限流的規則是在任意1s內,接口的請求次數都不能大於K次,維護一個大小爲K+1的循環隊列,用來記錄1s內到來的請求,這裏的循環隊列大小等於限流次數+1,因爲循環隊列存儲數據時會浪費一個存儲單元,當有新請求到來的時候,將與這個新請求的時間間隔超過1s的請求,從隊列中刪除,然後再來看循環隊列中是否有空閒位置,如果有,把新請求存儲在隊列尾部,如果沒有說明這1秒內的請求次數已經超過限流值K,拒絕服務

即使滑動時間窗口限流算法可以保證任意時間窗口內,接口請求次數都不會超過最大限流值,但是不能防止,在細時間粒度上訪問過於集中問題,比如在第一個1秒的時間窗口內,100次請求都集中在最後10毫秒中,即基於時間窗口的限流算法,不管是固定時間窗口還是滑動時間窗口,只能在選定的時間粒度上限流,對選定時間粒度內的更加細粒度的訪問頻率不做限制

時間粒度上訪問過於集中問題,比如在第一個1秒的時間窗口內,100次請求都集中在最後10毫秒中,即基於時間窗口的限流算法,不管是固定時間窗口還是滑動時間窗口,只能在選定的時間粒度上限流,對選定時間粒度內的更加細粒度的訪問頻率不做限制

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