基於客戶端 ID 的速率限制
使用 ClientRateLimit 中介軟體,你可以為不同的方案設定多個限制,例如允許客戶端在每秒,15 分鐘等時間間隔內進行最大數量的呼叫。你可以定義這些限制以解決對 API 或你的所有請求可以限制每個 URL 路徑或 HTTP 動詞和路徑的限制。
建立
NuGet 安裝 :
Install-Package AspNetCoreRateLimit
Startup.cs 程式碼 :
public void ConfigureServices(IServiceCollection services)
{
// needed to load configuration from appsettings.json
services.AddOptions();
// needed to store rate limit counters and ip rules
services.AddMemoryCache();
//load general configuration from appsettings.json
services.Configure<ClientRateLimitOptions>(Configuration.GetSection("ClientRateLimiting"));
//load client rules from appsettings.json
services.Configure<ClientRateLimitPolicies>(Configuration.GetSection("ClientRateLimitPolicies"));
// inject counter and rules stores
services.AddSingleton<IClientPolicyStore, MemoryCacheClientPolicyStore>();
services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>();
// Add framework services.
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseClientRateLimiting();
app.UseMvc();
}
你應該在除 loggerFactory 之外的任何其他元件之前註冊中介軟體。
如果你對應用程式進行負載平衡,則需要將 IDistributedCache
與 Redis 或 SQLServer 一起使用,以便所有的 kestrel 例項都具有相同的速率限制儲存。你應該像這樣注入分散式儲存,而不是在記憶體儲存中:
// inject counter and rules distributed cache stores
services.AddSingleton<IClientPolicyStore, DistributedCacheClientPolicyStore>();
services.AddSingleton<IRateLimitCounterStore,DistributedCacheRateLimitCounterStore>();
配置和一般規則 appsettings.json :
"ClientRateLimiting": {
"EnableEndpointRateLimiting": false,
"StackBlockedRequests": false,
"ClientIdHeader": "X-ClientId",
"HttpStatusCode": 429,
"EndpointWhitelist": [ "get:/api/license", "*:/api/status" ],
"ClientWhitelist": [ "dev-id-1", "dev-id-2" ],
"GeneralRules": [
{
"Endpoint": "*",
"Period": "1s",
"Limit": 2
},
{
"Endpoint": "*",
"Period": "15m",
"Limit": 100
},
{
"Endpoint": "*",
"Period": "12h",
"Limit": 1000
},
{
"Endpoint": "*",
"Period": "7d",
"Limit": 10000
}
]
}
如果 EnableEndpointRateLimiting
設定為 false
,則限制將全域性應用,並且僅適用作為端點*
的規則。例如,如果你設定每秒 5 次呼叫的限制,則對任何端點的任何 HTTP 呼叫都將計入該限制。
如果 EnableEndpointRateLimiting
設定為 true
,則限制將適用於 {HTTP_Verb}{PATH}
中的每個端點。例如,如果你為*:/api/values
設定每秒 5 個呼叫的限制,則客戶端可以每秒呼叫 GET /api/values
5 次,但也可以呼叫 5 次 PUT /api/values
。
如果 StackBlockedRequests
設定為 false
,拒絕的呼叫不會新增到節流計數器。如果客戶端每秒發出 3 個請求並且你設定了每秒一個呼叫的限制,則其他限制(如每分鐘或每天計數器)將僅記錄第一個呼叫,即未阻止的呼叫。如果你希望被拒絕的請求計入其他限制,則必須將 StackBlockedRequests
設定為 true
。
ClientIdHeader
用於提取客戶端 ID,如果此標頭中存在客戶端 ID 並且與 ClientWhitelist 中指定的值匹配,則不應用速率限制。
覆蓋特定客戶端 appsettings.json 的一般規則 :
"ClientRateLimitPolicies": {
"ClientRules": [
{
"ClientId": "client-id-1",
"Rules": [
{
"Endpoint": "*",
"Period": "1s",
"Limit": 10
},
{
"Endpoint": "*",
"Period": "15m",
"Limit": 200
}
]
},
{
"Client": "client-id-2",
"Rules": [
{
"Endpoint": "*",
"Period": "1s",
"Limit": 5
},
{
"Endpoint": "*",
"Period": "15m",
"Limit": 150
},
{
"Endpoint": "*",
"Period": "12h",
"Limit": 500
}
]
}
]
}
定義速率限制規則
規則由端點,期間和限制組成。
端點格式為 {HTTP_Verb}:{PATH}
,你可以使用 asterix 符號來定位任何 HTTP 謂詞。
期間格式為 {INT}{PERIOD_TYPE}
,你可以使用以下期間型別之一:s, m, h, d
。
限制格式為 {LONG}
。
示例 :
將所有端點的速率限制為每秒 2 次呼叫:
{
"Endpoint": "*",
"Period": "1s",
"Limit": 2
}
如果在同一秒內,客戶端對 api /值進行 3 次 GET 呼叫,則最後一次呼叫將被阻止。但是如果在同一秒內他也呼叫了 PUT api /值,那麼請求將會通過,因為它是一個不同的端點。當啟用端點速率限制時,每個呼叫都根據 {HTTP_Verb}{PATH}
進行速率限制。
使用任何 HTTP 動詞限制呼叫,每 15 分鐘撥打 22 到 5 個呼叫:
{
"Endpoint": "*:/api/values",
"Period": "15m",
"Limit": 5
}
速率限制每小時 5 個呼叫:
{
"Endpoint": "get:/api/values",
"Period": "1h",
"Limit": 5
}
如果在一小時內,客戶端對 api /值進行 6 次 GET 呼叫,則最後一次呼叫將被阻止。但是如果在同一個小時內他也呼叫了 GET api / values / 1,那麼請求將會通過,因為它是一個不同的端點。
行為
當客戶端進行 HTTP 呼叫時,ClientRateLimitMiddleware 執行以下操作:
- 從請求物件中提取客戶端 ID,HTTP 謂詞和 URL,如果要實現自己的提取邏輯,可以覆蓋
ClientRateLimitMiddleware.SetIdentity
- 在白名單中搜尋客戶端 ID 和 URL,如果有匹配則不執行任何操作
- 在客戶端規則中搜尋匹配項,所有適用的規則按期間分組,對於每個期間使用最嚴格的規則
- 在匹配的常規規則中搜尋,如果匹配的一般規則具有客戶端規則中不存在的已定義時間段,則也會使用此一般規則
- 對於每個匹配規則,速率限制計數器遞增,如果計數器值大於規則限制,則請求被阻止
如果請求被阻止,則客戶端會收到如下文字響應:
Status Code: 429
Retry-After: 58
Content: API calls quota exceeded! maximum admitted 2 per 1m.
你可以通過更改這些選項 HttpStatusCode
和 QuotaExceededMessage
來自定義響應,如果你想要實現自己的響應,則可以覆蓋 ClientRateLimitMiddleware.ReturnQuotaExceededResponse
。Retry-After
標頭值以秒錶示。
如果請求沒有得到速率限制,那麼匹配規則中定義的最長週期用於組成 X-Rate-Limit 標頭,這些標頭將在響應中注入:
X-Rate-Limit-Limit: the rate limit period (eg. 1m, 12h, 1d)
X-Rate-Limit-Remaining: number of request remaining
X-Rate-Limit-Reset: UTC date time when the limits resets
預設情況下,使用 Microsoft.Extensions.Logging.ILogger
記錄阻止請求,如果要實現自己的記錄,可以覆蓋 ClientRateLimitMiddleware.LogBlockedRequest
。當請求獲得速率限制時,預設記錄器會發出以下資訊:
info: AspNetCoreRateLimit.ClientRateLimitMiddleware[0]
Request get:/api/values from ClientId client-id-1 has been blocked, quota 2/1m exceeded by 3. Blocked by rule *:/api/value, TraceIdentifier 0HKTLISQQVV9D.
在執行時更新速率限制
在應用程式啟動時,appsettings.json
中定義的客戶端速率限制規則由 MemoryCacheClientPolicyStore
或 DistributedCacheClientPolicyStore
載入到快取中,具體取決於你使用的快取提供程式型別。你可以訪問控制器內的客戶端策略儲存並修改規則,如下所示:
public class ClientRateLimitController : Controller
{
private readonly ClientRateLimitOptions _options;
private readonly IClientPolicyStore _clientPolicyStore;
public ClientRateLimitController(IOptions<ClientRateLimitOptions> optionsAccessor, IClientPolicyStore clientPolicyStore)
{
_options = optionsAccessor.Value;
_clientPolicyStore = clientPolicyStore;
}
[HttpGet]
public ClientRateLimitPolicy Get()
{
return _clientPolicyStore.Get($"{_options.ClientPolicyPrefix}_cl-key-1");
}
[HttpPost]
public void Post()
{
var id = $"{_options.ClientPolicyPrefix}_cl-key-1";
var clPolicy = _clientPolicyStore.Get(id);
clPolicy.Rules.Add(new RateLimitRule
{
Endpoint = "*/api/testpolicyupdate",
Period = "1h",
Limit = 100
});
_clientPolicyStore.Set(id, clPolicy);
}
}
這樣,你可以將客戶端速率限制儲存在資料庫中,並在每個應用程式啟動後將其推送到快取中。