idou老師教你學istio30:Mixer Redis Quota Adapter 實現和機制

1. 配置

1.1參數

0404_1.jpg

1.2 Params.Quota

0404_2.jpg

1.3Params.Override

0404_3.jpg

1.4Params.QuotaAlgorithm

速率限制的算法:

Fixed Window 算法每個時間間隔對應一個計數器,每當有請求到來,如果此時計數器未達到配額的限定值,則計數器加 1,否則拒絕服務。當進入下一個時間間隔時,計數器失效被重置。該算法的缺點在於不能保證在任意的時間間隔內,速率都被限制在配額以下。即如果請求集中在計數器失效的時間點附近,則在該時間點附近的時間間隔內,速率最大能達到配額的兩倍。

Rolling Window 算法通過對上一個時間間隔請求數和當前時間間隔已處理的請求數進行加權,實現了對任意時間間隔的速率的估算。

0404_4.jpg

圖片來自

https://blog.cloudflare.com/counting-things-a-lot-of-different-things/

如上圖所示,在上一分鐘內處理了 42 個請求,當前這一分鐘已經過去了 15 秒,處理了 18 個請求,則當前這一分鐘的速率可以估算爲:

rate = 42 * ((60-15)/60) + 18 = 42 * 0.75 + 18 = 49.5

如果使用 memquota adapter,默認使用亞秒級分辨率的 rolling window 實現速率限制。

如果使用 redisquota adapter,可以配置使用 Rolling Window 算法或者 Fixed Window 算法。

0404_5.jpg

1.5例子

0404_6.jpg

上面的 redisquota handler 定義了三種不同的速率限制模式:

  • 如果沒有 override 能夠匹配,默認每秒限制 500 次請求;

  • 如果請求的 destination 是 reviews,每秒限制 1 次請求;

  • 如果請求的 destination 是 productpage,每秒限制 2 次請求。

2. 代碼分析

2.1Mixer Server 啓動時註冊 Check 接口的流程

首先從 Mixer Server 的啓動入口 main() 函數看起,在 istio/mixer/cmd/mixs/main.go 中:

0404_7.jpg

在 main() 函數中調用了 GetRootCmd() 方法,獲取 cobra 命令樹的根節點。

0404_8.jpg

在 GetRootCmd() 方法中添加了 serverCmd() 命令。

0404_9.jpg

在 serverCmd() 中定義了,當我們執行 mixs server 命令時,會調用 runServer() 函數,將 mixer 作爲一個 gRPC server 啓動。

0404_10.jpg

在 runServer() 中,調用了 server.New() 方法,初始化了一個具備全部功能的 Mixer server,可以開始接收流量。

0404_11.jpg

在 server.New() 方法中,調用了 newServer() 函數。

0404_12.jpg

在 newServer() 函數中,通過調用 grpc.NewServer() 方法,創建了一個 gRPC server,但是尚未註冊服務,而且未啓動去接收請求。然後調用了 RegisterMixerServer() 函數註冊 Mixer 服務。

0404_13.jpg

在 RegisterMixerServer() 函數中,調用了上面創建的 gRPC server 的 RegisterService() 方法在該 gRPC server 中註冊 Mixer 服務。該方法的參數 _Mixer_serviceDesc 是對 Mixer 服務的描述。

0404_14.jpg

在對 Mixer 服務的描述中可以看出,該服務提供了兩個接口,分別是 Check 和 Report。限流功能調用的是 Check 接口。

0404_15.jpg

在處理 Check 請求的 handler 中,定義了該接口的 RPC 方法全稱,並且調用了 MixerServer 接口提供的 Check() 方法。

0404_16.jpg

MixerServer 接口定義了 Mixer 服務提供的 API。Check接口在執行對服務的調用之前,預先對一些條件進行檢查,並分配配額。

2.2運行時 Mixer 處理 Check 接口調用的流程

0404_17.jpg

如上圖所示,在 istio/mixer/pkg/api/grpcServer.go 中實現了 MixerServer 接口。

0404_18.jpg

在 MixerServer 接口的 Check() 方法的實現中,調用了 check() 方法。

0404_19.jpg

0404_20.jpg

在 check() 方法中調用了 Dispatcher 接口的 Quota() 方法。

0404_21.jpg

如上圖所示是 Dispatcher 接口的定義,Quota() 方法將請求分發到與 Quota API 相關的 adapter。

0404_22.jpg

上圖所示是 Quota() 方法在運行時的實現,在實現時調用了 dispatch() 方法。

0404_23.jpg

0404_24.jpg

0404_25.jpg

在 dispatch() 方法中,通過 session 的 variety 字段區分 preprocess、check、report 或 quota 幾種操作類型。如果是 quota 操作,首先匹配和 quota 的名稱對應的 instance,然後調用 dispatchToHandler() 方法對匹配到的 instance 進行分發。

0404_26.jpg

在 dispatchTohandler() 方法中,調用了 ScheduleWork() 方法,註冊 invokeHandler() 方法,使其在某個時間點被調用。

0404_27.jpg

0404_28.jpg

在 invokeHandler() 方法中,通過調用 DispatchQuota() 方法,將 instace 分發到 Redis Quota adapter。

2.3Mixer Server啓動時註冊DispatchQuota() 函數流程

0404_29.jpg

在 Mixer Server 的啓動入口 main() 函數中,調用 supportedTemplates() 函數傳入了支持的模板。

0404_30.jpg

supportedTemplates() 函數返回 generatedTmplRepo.SupportedTmplInfo。

0404_31.jpg

0404_32.jpg

在 generatedTmplRepo 包中,將 SupportedTmplInfo 的 DispatchQuota 字段定義爲上述函數,在該函數中調用了 Redis Quota Adatper 的 HandleQuota() 方法。

2.4Redis Quota Adapter 的實現

2.4.1結構定義

首先定義 handler 結構如下:

0404_33.jpg

其中 client 字段是一個 Redis client,用於連接存放 quota 信息的 Redis 數據庫;limits 字段用於存放用戶配置的限制;dimensionHash 字段用於存放用戶定義的 override 的 dimension 的 hash;scripts 字段用於存放兩種限流算法的 Lua 腳本供 Redis 使用;getTime() 用於獲取當前時間。

0404_34.jpg

builder 結構用於構建 handler,包含 quotaTypes 和 adapterConfig 兩個字段。adapterConfig 字段用於存放構建 handler 時需要的參數。

0404_35.jpg

Params 結構定義如上,Quotas 字段是用戶定義的限制,RedisServerUrl 字段存放 Redis server 的地址,ConnectionPoolSize 字段存放到 Redis 的最大的 idle 連接數。

0404_36.jpg

Params_Quota 結構定義如上。Name 字段是 quota 的名稱;MaxAmount 是分配 quota 數量的上限;ValidDuration 是分配的 quota 有效的時間;BucketDuration 字段僅在 RollingWindow 算法中使用,是窗口滑動的時間間隔;RateLimitAlgorithm 字段是限流算法的名稱,默認值是 FIXED_WINDOW;Overrides 字段定義了在某些條件下對默認規則的覆蓋。

0404_37.jpg

Params_Override 結構定義如上。Dimensions 字段定義了該 override 適用的條件,MaxAmount 定義了該 override 下分配 quota 數量的上限。

2.4.2啓動時的配置方法

0404_38.jpg

Validate() 方法對 builder 的參數進行校驗,並檢查 Redis 是否能連通,以及 Lua 腳本是否被導入 Redis。

0404_39.jpg

0404_40.jpg

Build() 方法通過 builder 構建 handler。

0404_41.jpg

getDimensionHash() 方法計算 dimension 的 hash,在 Build() 方法中被調用,用於構建 handler。

2.4.3運行時方法

在 2.3 節中提到,Redis Quota Adapter 運行時的入口是 HandleQuota() 方法,該方法可以分爲 3 個部分。

0404_42.jpg

首先是調用 getKeyAndQuotaAmount() 方法,獲取該 instance 適用的 key 和 maxAmount。

0404_43.jpg

getKeyAndQuotaAmount() 方法實現如上。如果該 instance 沒有任何 override 能夠匹配,那麼 key 爲 quota 的名稱,maxAmount 爲該 quota 默認的 maxAmount。如果匹配到了某個 override,那麼 key 爲 quota 的名稱加上該 override 的 dimension 的 hash,maxAmount 爲該 override 的 maxAmount。

0404_44.jpg

第二步是調用 Redis 中的限流算法的 Lua 腳本,根據配置信息和參數信息,獲取運行結果。

0404_45.jpg

然後調用 getAllocatedTokenFromResult() 函數,將 Redis 的返回結果轉換成分配的 token 數和有效時間,返回最終結果。

0404_46.jpg

getAllocatedTokenFromResult() 函數定義如上,Redis 的返回結果應該有兩個值,第一個值爲分配的 token 數,第二個值爲 token 有效的時間。

相關服務請訪問:https://support.huaweicloud.com/cce/index.html?utm_content=cce_helpcenter_2019

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