首頁>技術>

限流一般是為了解決因擁擠導致服務無法正常提供的問題。比如常見的地鐵限流,如果很多人在很短的時間內快速湧入,超出站臺內的空間或列車的可運載人數,就會容易引發安全事故。

在github上使用C#編寫的限流程式有很多,翻看了一些,大部分都有很多的場景限制,比如只能用在asp.net core,或者只能使用IP或者ClientId限制,或者對分散式部署不友好,又或者支援的限流演算法比較單一。

FireflySoft.RateLimit的目的是提供一個更為基礎的限流元件,可以用於各種限流業務場景,可以用於多種形式的程式,可以容納多種限流演算法,支援分散式統一限流,支援靈活的限流目標控制,提供方便的自定義機制,包括各種配置、演算法和資料持久化等,同時使用起來更為簡便。

下面先簡單介紹其使用方法,然後再看一下其邏輯原理。

使用方法

以一個簡單的ASP.NET Core WebAPI固定視窗限流為例,要求每個介面每秒鐘限制50次請求。使用FireflySoft.RateLimit只需兩步:安裝Nuget包、使用限流中介軟體。

1、安裝Nuget包

透過包管理器安裝:

Install-Package FireflySoft.RateLimit.AspNetCore -Version 1.0.0

或者透過.NET CLI:

dotnet add package FireflySoft.RateLimit.AspNetCore --version 1.0.0

當然你也可以自己編譯程式碼,新增本地引用,開源專案地址:https://github.com/bosima/FireflySoft.RateLimit

2、使用限流中介軟體

開啟 Startup.cs,在Configure方法中新增 app.UseRateLimit,並提供限流的相關設定,包括:限流的請求型別、限流使用的演算法、限流的規則、限流計數的持久化方式、限流時的錯誤訊息等等。示例程式碼如下:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env){    ...    app.UseRateLimit(new Core.RateLimitProcessor<HttpContext>.Builder()        // .WithError(new Core.RateLimitError()        // {        //     Code=429,        //     Message = "The system is busy, please try again later"        // })        // .WithStorage(new RedisStorage(StackExchange.Redis.ConnectionMultiplexer.Connect("localhost")))        .WithAlgorithm(new FixedWindowAlgorithm<HttpContext>( new[] {            new FixedWindowRateLimitRule<HttpContext>()            {                ExtractTarget = context =>                {                    // for all path, you can customize it                    return "rule1-" + context.Request.Path.Value;                },                CheckRuleMatching = context =>                {                    // limit every request, you can customize it                    return true;                },                Name="general limit rule",                LimitNumber=30,                LockSeconds=1,                StatWindow=TimeSpan.FromSeconds(1)            }        }))        .Build());    ...}

複製上邊的程式碼到你的專案中,開啟Postman,發起一個Runner,馬上就可以體驗到FireflySoft.RateLimit的限流效果。

FireflySoft.RateLimit還提供了另外兩個Nuget包:

FireflySoft.RateLimit.AspNet用於基於.NET Framework的ASP.NET專案限流,.NET版本必須是4.6.1及以上。FireflySoft.RateLimit.Core是FireflySoft.RateLimit.的核心類庫,可以整合到任何型別的專案中進行限流處理。不僅僅是Web服務限流,各種需要數量限制的業務處理都可以使用。邏輯原理

1、限流處理器

使用FireflySoft.RateLimit首先需要建立一個限流處理器RateLimitProcessor的例項,然後用它來過濾每一個請求。

建立RateLimitProcessor的例項,至少需要提供兩個資訊:被限流的請求型別、限流演算法和規則。其它還有限流計數持久化方式、限流錯誤資訊,如果不提供會使用預設值。這裡先不展開,後邊會詳細介紹它們。

建立RateLimitProcessor需要採用構造器模式,如果你使用ASP.NET Core應該很熟悉,如果沒什麼印象開啟Program.cs就可以看到了。

為了提高效率,這個限流處理器一般會統一放在某個過濾器中,這個過濾器在ASP.NET中可以是一個訊息處理器,在ASP.NET Core中一般就是一箇中間件。為了方便整合,FireflySoft.RateLimit已經提供了一個基於ASP.NET的訊息處理器和一個基於ASP.NET Core的中介軟體,透過Github和Nuget都可以很方便的找到他們,Github上還提供了演示程式。

2、被限流請求型別

被限流請求型別就是需要被限制的業務請求的型別,在ASP.NET中他可能是HttpRequestMessage,在ASP.NET Core中它可能是HttpContext,在其它業務中它可能是你自定義的一個型別XXXRequest。

被限流請求型別很重要,因為不同的業務可能使用不同的請求型別,而限流處理程式需要從當前請求中提取或關聯到限流目標,明確請求型別才能確定提取方法,否則對請求進行限制將無所適從。

3、限流演算法和規則

FireflySoft.RateLimit提供了四種常見的限流演算法:固定視窗、滑動視窗、漏桶、令牌桶。固定視窗相對最簡單,無論是演算法的時間複雜度還是分散式環境實現難度都比較有優勢,如果需求是在較短的時間內進行限制,比如每秒限制多少次,使用這種演算法是最合適的。但是實際場景中請求在時間分佈中可能不太均勻,時多時少,根據需求的不同,可能需要選擇其它三種限流演算法,這裡不做說明了,網上已經有很多的場景選擇說明。

初始化限流演算法還需要提供對應的限流規則,因為不同的演算法往往需要不同的引數,這裡很難對不同的演算法提供完全統一的限流規則,不過這些規則確實是有共同設定項的,比如限流鎖定時間、目標提取方法、是否應用限流處理的判斷方法等。

以上文【使用方法】中的限流程式碼為例,做一些介紹:

FixedWindowAlgorithm是此限流元件提供的固定視窗限流演算法。FixedWindowRateLimitRule是此限流元件提供的固定視窗限流規則型別;HttpContext是業務中需要限制的請求型別,這裡是Http請求上下文型別;StatWindow是固定視窗的大小,是一個時間跨度;LimitNumber是限流值,在StatWindow時間內請求數超過它就會觸發限流;LockSeconds是觸發限流後的鎖定時間,此時間內請求都會被阻止,不需要鎖定時可以不設定;ExtractTarget傳遞一個方法用於從請求中提取限流目標,這裡是使用者的每一個請求路徑;CheckRuleMatching傳遞一個方法用於檢查當前請求是否需要限流檢查,這裡return true代表所有請求都要經過限流檢查。

這裡有兩個比較有意思的設定:ExtractTarget和CheckRuleMatching,他們共同作用,讓使用者可以完全自由的定製自己限流的目標和條件,不固定是IP、ClientId或者Url。其它演算法規則中每個設定項的含義可以透過其註釋瞭解到。

如果這幾個演算法還不能滿足要求,可以透過實現IRateLimitAlgorithm來定義一個新的演算法。

4、計數持久化方式

FireflySoft.RateLimit中的限流計數目前支援儲存在記憶體或者Redis中,也可以透過實現IRateLimitStorage來定義一個新的儲存器。

對於只需要部署一份的程式,絕大部分情況下使用記憶體就夠了;但是如果限流的時間視窗比較長,比如1小時限制300次,重啟就會丟失計數,這可能是個風險,此時使用Redis會比較合適。

對於需要部署多份的程式,如果請求是基本均勻的,並且在每個部署之間的分配也是均勻的,那麼使用記憶體儲存器也未嘗不可,將總的限流數平均分配在每一個部署中,只要有一個部署觸發限流,其它部署很大機率上也會觸發限流。但是對於需要部署多份的情況,採用更持久的Redis方式才是穩妥的。

相比記憶體訪問,Redis訪問需要網路互動,這會造成一定的效能損失,訪問量很大時也會產生擁堵,不過也可以將請求分散到多個Redis的方式進行緩解;同時分散式環境下資料一致性的實現難度更大,即使使用Redis,比如限流處理中必然會涉及的各種時間,不同節點之間的時間不可能絕對一致,越短的時間視窗協調難度越大。因此使用滑動視窗、漏桶、令牌桶等分散式實現難度較大的演算法時,需要注意時間單位的設定。

5、限流錯誤資訊

限流錯誤資訊是觸發限流時限流檢查結果中附帶的資訊,目前每個限流處理器允許設定一個統一的RateLimitError,方便業務側進行統一處理。預設限流錯誤Code是429,Message為null。你也可以不使用這個定義的值,根據當前限流目標和限流規則構造自己的錯誤資訊。

6、限流鎖定

限流鎖定是觸發限流時的懲罰性處理。FireflySoft.RateLimit透過設定限流規則中的LockSeconds,定義觸發限流後的鎖定時間,此時間內的請求都會被認定觸發限流規則,而不被允許透過;如果不需要鎖定忽略這個設定就可以了。

瞭解原始碼

FireflySoft.RateLimit已經在Github開源:https://github.com/bosima/FireflySoft.RateLimit

包含的專案如下:

FireflySoft.RateLmit.Core

FireflySoft.RateLmit的演算法、規則、持久化和其它核心程式碼。

FireflySoft.RateLmit.Core.Sample

使用FireflySoft.RateLmit.Core的示例程式。

FireflySoft.RateLimit.AspNet

為基於 .NET Framework的ASP.NET提供的限流元件。

FireflySoft.RateLimit.AspNet.Sample

使用FireflySoft.RateLimit.AspNet的示例程式。

FireflySoft.RateLimit.AspNetCore

ASP.NET Core限流中介軟體。

FireflySoft.RateLimit.AspNetCore.Sample

使用FireflySoft.RateLimit.AspNetCore的示例程式。

21
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 單個java程序佔用700%的CPU