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.RateLimit
總結
到此這篇關於ASP.NET Core對不同類型的用戶進行區別限流的文章就介紹到這瞭,更多相關ASP.NET Core用戶區別限流內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- ASP.NET Core中使用滑動窗口限流的問題及場景分析
- 解決ASP.NET Core中使用漏桶算法限流的問題
- 如何在ASP.NET Core中使用Session的示例代碼
- ASP.NET Core 使用Cookie驗證身份的示例代碼
- .NET CORE 鑒權的實現示例