我 们 将 了 解 如 何 使 用 不 同 的 速 率 限 制 算 法 , 并 在 .NET Web API 中 使 用 Microsoft.AspNetCore.RateLimiting NuGet 包实现它们。要了解有关速率限制基础知识的更 多信息,请阅读下面的博客。 下面是 Microsoft.AspNetCore.RateLimiting 中间件中可用的不同速率限制算法:
- Fixed Window
- Sliding Window
- Token Bucket
- Concurrency
Fixed Window算法
该算法计算在固定时间窗口内发出的请求数。在每个时间窗口开始时,请求计数器将重置为零。 每个传入的请求都会递增计数器。 优点 . 实施简单。 . 易于理解。 缺点 容易受到每个时间窗口开始时请求的突发攻击 下面是我们如何配置固定窗口算法并在端点级别使用它的示例。
// Add services to the container.
builder.Services.AddRateLimiter(options => {
options.AddFixedWindowLimiter("Fixed", opt => {
opt.Window = TimeSpan.FromSeconds(10); opt.PermitLimit = 4;
opt.QueueLimit = 2;
opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; });
options.RejectionStatusCode = 429; });
app.UseRateLimiter();
[HttpGet(Name = "GetWeatherForecastWithFixedRateAlgorithm")] [EnableRateLimiting("Fixed")]
public IEnumerable<WeatherForecast> GetWithFixedRateAlgorithm() {
return Enumerable.Range(1, 5).Select(index => new WeatherForecast {
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)] })
.ToArray(); }
Sliding Window算法
此算法跟踪滑动时间窗口内最近请求的时间戳。维护每个请求的时间戳日志,然后删除滑动窗口 外的时间戳。滑动窗口限制器的操作方式与固定窗口限制器非常相似,但在每个窗口内引入段。 每个段都按照段间隔一个接一个地向前移动。此段间隔的计算方式为窗口时间除以每个窗口的段 数。
优点
允许随着时间的推移实现更顺畅的速率限制。
缺点
. 需要更多内存来存储时间戳。 下面是滑动窗口将如何进行以及总请求限制将如何随着时间的推移而变化的图示。请注意,下面 的传入请求位于段的开头,做出此假设是为了便于下面的解释。
下面是我们如何配置上述滑动窗口算法并在端点级别使用它的示例。
// Add services to the container.
builder.Services.AddRateLimiter(options => {
options.AddSlidingWindowLimiter("Sliding", opt => { opt.PermitLimit = 100;
opt.Window = TimeSpan.FromMinutes(30); opt.SegmentsPerWindow = 3;
opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; opt.QueueLimit = 10;
});
options.RejectionStatusCode = 429; });
app.UseRateLimiter();
[HttpGet(Name = "GetWeatherForecastWithSlidingAlgorithm")] [EnableRateLimiting("Sliding")]
public IEnumerable<WeatherForecast> GetWithSlidingAlgorithm() {
return Enumerable.Range(1, 5).Select(index => new WeatherForecast {
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)] })
.ToArray(); }
Token Bucket 算法
允许激增至一定限制的请求,并以固定速率重新填充。请求使用存储桶中的令牌,而存储桶以恒 定的速率重新填充。令牌桶限制器类似于滑动窗口限制器,但不是将过期段中获取的请求加回, 而是在每个补充期添加固定数量的令牌。每个段添加的令牌不能将可用令牌增加到高于令牌桶限 制的数字。
优点
允许突发请求,同时保持总体限制。
缺点
需要跟踪令牌充值的时间戳。 下表显示了令牌桶限制器,其限制为 100 个令牌,补充周期为 10 秒。
以下是我们如何配置上述令牌桶算法并在端点级别使用它的示例。
// Add services to the container.
builder.Services.AddRateLimiter(options => {
options.AddTokenBucketLimiter("Token", opt => {
opt.TokenLimit = 100;
opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; opt.QueueLimit = 10;
opt.ReplenishmentPeriod = TimeSpan.FromSeconds(10);
opt.TokensPerPeriod = 10; //Rate at which you want to fill opt.AutoReplenishment = true;
});
options.RejectionStatusCode = 429; });
app.UseRateLimiter();
[HttpGet(Name = "GetWeatherForecastWithTokenAlgorithm")] [EnableRateLimiting("Token")]
public IEnumerable<WeatherForecast> GetWithTokenAlgorithm() {
return Enumerable.Range(1, 5).Select(index => new WeatherForecast {
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)] })
.ToArray();
}
Concurrency算法
并发限制是最简单的速率限制形式,侧重于正在处理的并发请求数。并发限制器限制并发请求的 数量。每个请求将并发限制减少 1。当请求完成时,限制将增加 1。与限制指定时间段内请求总 数的其他请求限制器不同,并发限制器仅限制并发请求数,而不限制间隔内的请求数。 下面是我们如何配置上述并发算法并在端点级别使用它的示例。
// Add services to the container.
builder.Services.AddRateLimiter(options => {
options.AddConcurrencyLimiter("Concurrency", opt => {
opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; opt.QueueLimit = 10;
opt.PermitLimit = 100; });
options.RejectionStatusCode = 429; });
app.UseRateLimiter();
[HttpGet(Name = "GetWeatherForecastWithConcurrency")] [EnableRateLimiting("Concurrency")]
public IEnumerable<WeatherForecast> GetWithConcurrencyAlgorithm() {
return Enumerable.Range(1, 5).Select(index => new WeatherForecast {
Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)] })
.ToArray(); }