1. 程式人生 > >ASP.NET Core中如何對不同型別的使用者進行區別限流

ASP.NET Core中如何對不同型別的使用者進行區別限流

老闆提出了一個新需求,從某某天起,免費使用者每天只能查詢100次,收費使用者100W次。

這是一個限流問題,聰明的你也一定想到了如何去做:記錄使用者每一天的查詢次數,然後根據當前使用者的型別使用不同的數字做比較,超過指定的數字就返回錯誤。

嗯,原理就是這麼簡單。不過真正寫起來還要考慮更多問題:

  • 統計資料的資料結構是什麼樣的?字典 or 行記錄?
  • 統計資料記錄到哪裡?記憶體 or MySQL or Redis?
  • 分散式應用怎麼精確計數?分散式鎖 or 佇列 or 事務?
  • 吞吐量比較大時如何扛得住?記憶體 or Redis or 資料庫叢集?
  • 這些資料要一直保留嗎?自動過期 or 定期清理?
  • 如何返回錯誤?自定義錯誤 or HTTP標準錯誤碼?

自己去做這些事還是有點麻煩的,這裡介紹一個ASP.NET Core的中介軟體來滿足這個限流需求:FireflySoft.RateLimit.AspNetCore。使用步驟如下:


 

1、安裝Nuget包

已經發布到nuget.org,有多種安裝方式,選擇自己喜歡的就行了。

包管理器命令:

Install-Package FireflySoft.RateLimit.AspNetCore

或者.NET命令:

dotnet add package FireflySoft.RateLimit.AspNetCore

或者專案檔案直接新增:

<ItemGroup>
<PackageReference Include="FireflySoft.RateLimit.AspNetCore" Version="1.2.0" />
</ItemGroup>

 

2、使用中介軟體

在Startup.Configure中使用中介軟體,演示程式碼如下(下邊會有詳細說明):

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    ...

    app.UseRateLimit(new RateLimitProcessor<HttpContext>.Builder()
        .WithAlgorithm(new FixedWindowAlgorithm<HttpContext>( new[] {
            new FixedWindowRateLimitRule<HttpContext>()
            {
                Id = "1",
                ExtractTarget = context =>
                {
                    // 這裡假設使用者Id是從cookie中傳過來的,需根據實際情況獲取
                    return context.Request.GetTypedHeaders().Get<string>("userId");
                },
                CheckRuleMatching = context =>
                {
                    // 這裡假設使用者型別是從cookie中傳過來的,實際可能需要根據使用者Id再去查詢
                    // 0免費使用者 1收費使用者
                    int userType = context.Request.GetTypedHeaders().Get<int>("userType");
                    if(userType==0){
                        return true;
                    }
                    return false;
                },
                Name="免費使用者限流規則",
                LimitNumber=100,
                StatWindow=TimeSpan.FromDays(1)
            },
            new FixedWindowRateLimitRule<HttpContext>()
            {
                Id = "2",
                ExtractTarget = context =>
                {
                    // 這裡假設使用者Id是從cookie中傳過來的,需根據實際情況獲取
                    return context.Request.GetTypedHeaders().Get<string>("userId");
                },
                CheckRuleMatching = context =>
                {
                    // 這裡假設使用者型別是從cookie中傳過來的,實際可能需要根據使用者Id再去查詢
                    // 0免費使用者 1收費使用者
                    int userType = context.Request.GetTypedHeaders().Get<int>("userType");
                    if(userType==1){
                        return true;
                    }
                    return false;
                },
                Name="收費使用者限流規則",
                LimitNumber=1000000,
                StatWindow=TimeSpan.FromDays(1)
            }
        }))
        .WithError(new Core.RateLimitError()
        {
            Code=429,
            Message = "查詢數達到當天最大限制"
        })
        //.WithStorage(new RedisStorage(StackExchange.Redis.ConnectionMultiplexer.Connect("localhost")))
        .Build());

    ...
}

 

使用此中介軟體需要構建一個名為RateLimitProcessor的限流處理器例項,指定限流處理的請求型別HttpContext,設定限流處理的三個方面:

限流使用的演算法以及對應的規則

限流演算法,根據這個需求使用固定視窗演算法就可以了,也稱為計數器演算法。此中介軟體還提供了滑動視窗演算法、漏桶演算法、令牌桶演算法,可以根據需要選擇。

不同的限流演算法有不同的限流規則型別,在這裡使用的是固定視窗限流規則,針對免費使用者和收費使用者分別定義了兩個規則,注意其中的幾個引數:

  • Id:在當前的版本中Id必須手動指定,並且不能重複。
  • ExtractTarget:傳遞一個方法用於從請求中提取限流目標,這裡就是使用者Id。
  • CheckRuleMatching傳遞一個方法用於檢查當前請求是否適用當前規則,這裡根據使用者型別進行判斷。
  • StatWindow是固定視窗的大小,是一個時間跨度,這裡是1天。
  • LimitNumber是限流值,在StatWindow時間內請求數超過它就會觸發限流。

這裡有兩個比較有意思的設定:ExtractTarget和CheckRuleMatching,他們共同作用,讓使用者可以完全自由的定製自己限流的目標和條件,無論是IP、ClientId或者Url。

限流統計資料的持久化方式

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

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

限流統計資料會根據限流時間視窗自動過期移除。

被限流時的錯誤碼和訊息

預設限流錯誤Code是429,這個會作為HttpStatusCode返回;Message預設為null,你可以修改為自己的任意文字提示,這個會作為Http Body的內容返回。


 

以上就是使用FireflySoft.RateLimit.AspNetCore對不同型別的使用者進行區別限流的使用方法。

如果覺得還是限制的有點死,比如返回錯誤資訊部分,想返回一個json格式的錯誤訊息,還可以使用FireflySoft.RateLimit.Core這個包來封裝自己的ASP.NET Core中介軟體。

如果想在這個程式的基礎上再改造下,可以fork這個專案:https://github.com/bosima/FireflySoft.RateL