主页 > 创业  > 

###net7+出现了自带的限流中间件固定窗口、滑动窗口并发令牌桶全局限流器

###net7+出现了自带的限流中间件固定窗口、滑动窗口并发令牌桶全局限流器

资料 限流的方法

速率限制算法

固定窗口算法 是最简单的算法之一。它将请求限制为一个固定的时间窗口,该窗口在任何时间点都只允许固定数量的请求。

滑动窗口算法 是固定窗口算法的改进版本,它将请求限制为一个可变的窗口,该窗口在任何时间点都只允许固定数量的请求。

令牌桶算法 使用固定大小的令牌桶来限制请求的速率。令牌桶最初被填满了指定数量的令牌。每次请求都会消耗一个令牌,如果令牌桶中没有令牌,则该请求会被拒绝。

并发算法 是一种非常简单的算法,它只允许固定数量的并发请求,但是不限制一段时间内的请求数。

IOC注册内置的限流中间件 //使用限流器 app.UseRateLimiter(); #region 限流中间件 IOC 注册 builder.Services.AddRateLimiter(options => { //1、固定窗口限流策略 //配置说明:该固定窗口5s时间内,可以最多有5+2=7个请求,5个会被处理,2个会被排队,其他则会在一定时间后拒绝返回 options.AddFixedWindowLimiter(policyName: "fixed", op => { op.PermitLimit = 5; //每个窗口时间范围内,允许100个请求被处理 op.Window = TimeSpan.FromSeconds(5);//窗口大小。即窗口时间长度5s。必须>TimeSpan.Zero //5s 内只能5个请求--过后继续 //排队请求的处理顺序。这里设置为有限处理先来的请求 op.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; //窗口阈值。即每个窗口时间范围内,最多允许的请求个数。该值必须>0。 //当窗口请求达到最大值,后续请求会进入排队。该值用于设置对垒大小(即允许几个请求在排队队列等待) op.QueueLimit = 2; //开启新窗口时是否自动重置请求限制,默认true。如果是false, //则需要手动调佣 FixedWindowRateLimiter.TryReplenish来重置 op.AutoReplenishment = true; }); }); #endregion 启用限流中间件 app.UseRateLimiter();//限流中间件 // 这个是所有都增加限流了 不是是特点路由限流了 //app.MapControllers().RequireRateLimiting("fixed"); app.MapControllers(); 按需加特性 [EnableRateLimiting("fixed")] 固定窗口限流器

滑动窗口

#region 限流中间件 IOC 注册 builder.Services.AddRateLimiter(options => { //2、滑动窗口限流则略 //配置说明:窗口时间长度为30s,每个窗口内,最多允许100个请求,窗口段数3,每个段的时间间隔为30/3=10s,即窗口每10s滑动一段。 options.AddSlidingWindowLimiter(policyName:"sliding", slidingOptions => { slidingOptions.PermitLimit = 100; slidingOptions.Window = TimeSpan.FromSeconds(30); slidingOptions.QueueLimit = 2; slidingOptions.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; slidingOptions.AutoReplenishment = true;//开启新窗口时是否自动重置请求限制,默认true slidingOptions.SegmentsPerWindow = 3; }); }); #endregion 令牌桶限流 #region 限流中间件 IOC 注册 builder.Services.AddRateLimiter(options => { //策略说明:桶最多装4个令牌,每10秒发放一次令牌,每次发放2个令牌,所以在一个发放周期, //最多可以处理4个请求(TokenLimit ),至少可以处理2个请求(TokensPerPeriod 一个周期发两个令牌)。 options.AddTokenBucketLimiter(policyName:"token_bucket", tokenBucketOptions => { tokenBucketOptions.TokenLimit = 4;//桶最多可以装的令牌数,发放的多余令牌会被丢弃 tokenBucketOptions.ReplenishmentPeriod = TimeSpan.FromSeconds(10);//令牌发放周期 tokenBucketOptions.TokensPerPeriod = 2;//每个周期发放令牌数 tokenBucketOptions.QueueLimit = 2;//当桶内的令牌全部被拿完(token=0)时,后续请求会进入排队 tokenBucketOptions.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; tokenBucketOptions.AutoReplenishment = true;//进入新令牌发放周期,是否自动发放令牌。如果设置为false,则需要手动调用 TokenBucketRateLimiter.TryReplenish来发放 }); }); #endregion 并发限流器

并发限流器不是限制一段时间内的最大请求数,而是限制并发数。

【原理】:限制同一时刻并发请求的数量。 【特点】:可以充分利用服务器的性能,当出现突发流量时,服务器负载可能会持续过高。 #region 限流中间件 IOC 注册 builder.Services.AddRateLimiter(options => { //策略说明:最大并发请求4,超过最大并发请求,则后续最多2个请求进入排队队列。 options.AddConcurrencyLimiter(policyName:"concurrency", concurrencyOptions => { concurrencyOptions.PermitLimit = 4;//最大并发请求数 concurrencyOptions.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; concurrencyOptions.QueueLimit = 2;//当并发请求数达到最大,后续请求进入排队,该参数用于配置队列大小 }); }); #endregion 全局限流器

通过GlobalLimiter,我们可以设置全局限流器,更准确的说法是全局分区限流器,该限流器会应用于所有请求。执行顺序为先执行全局限流器,再执行特定于路由终结点的限流器(如果存在的话)。

需要注意的是,相对于上面注册的限流策略来说,GlobalLimiter已经是一个限流器实例了,所以需要分配给他一个分区限流器实例,通过PartitionedRateLimiter.Create来创建。

builder.Services.AddRateLimiter(limiterOptions => { limiterOptions.GlobalLimiter = PartitionedRateLimiter.Create<HttpContext, IPAddress>(context => { IPAddress? remoteIpAddress = context.Connection.RemoteIpAddress; // 针对非回环地址限流 if (!IPAddress.IsLoopback(remoteIpAddress!)) { return RateLimitPartition.GetTokenBucketLimiter (remoteIpAddress!, _ => new TokenBucketRateLimiterOptions { TokenLimit = 4, QueueProcessingOrder = QueueProcessingOrder.OldestFirst, QueueLimit = 2, ReplenishmentPeriod = TimeSpan.FromSeconds(10), TokensPerPeriod = 10, AutoReplenishment = true }); } // 若为回环地址,则不限流 return RateLimitPartition.GetNoLimiter(IPAddress.Loopback); }); }); 链式组合限流器

builder.Services.AddRateLimiter(limiterOptions => { var chainedLimiter = PartitionedRateLimiter.CreateChained( PartitionedRateLimiter.Create<HttpContext, string>(httpContext => { var userAgent = httpContext.Request.Headers.UserAgent.ToString(); return RateLimitPartition.GetFixedWindowLimiter (userAgent, _ => new FixedWindowRateLimiterOptions { AutoReplenishment = true, PermitLimit = 4, Window = TimeSpan.FromSeconds(2) }); }), PartitionedRateLimiter.Create<HttpContext, string>(httpContext => { var userAgent = httpContext.Request.Headers.UserAgent.ToString(); return RateLimitPartition.GetConcurrencyLimiter (userAgent, _ => new ConcurrencyLimiterOptions { PermitLimit = 4, QueueProcessingOrder = QueueProcessingOrder.OldestFirst, QueueLimit = 2 }); }) limiterOptions.GlobalLimiter = chainedLimiter ; ); 注意 注意 注意

比如一个政策限流是 最大Limit=100 这个政策,分别加在两个路由中 [EnableRateLimiting(“xxx”)] 只有这个两个路由 请求 总和到达100,就会现在访问了。

下面我们就借助AddPolicy,分别使用两种方式添加一个自定义策略“my_policy”:一个用户一个分区,匿名用户共享一个分区。

通过委托创建自定义限流策略

builder.Services.AddRateLimiter(limiterOptions => { limiterOptions.AddPolicy(policyName: "my_policy", httpcontext => { var userId = "anonymous user"; if (httpcontext.User.Identity?.IsAuthenticated is true) { userId = httpcontext.User.Claims.First(c => c.Type == "id").Value; } return RateLimitPartition.GetFixedWindowLimiter(partitionKey: userId, _ => new FixedWindowRateLimiterOptions { PermitLimit = 3, Window = TimeSpan.FromSeconds(60), QueueProcessingOrder = QueueProcessingOrder.OldestFirst, QueueLimit = 0 }); }); });

通过IRateLimiterPolicy创建自定义限流策略

public interface IRateLimiterPolicy<TPartitionKey> { // 若不为空,则执行它(不会执行全局的),如果它为空,则执行全局的 Func<OnRejectedContext, CancellationToken, ValueTask>? OnRejected { get; } // 获取限流分区 RateLimitPartition<TPartitionKey> GetPartition(HttpContext httpContext); } public class MyRateLimiterPolicy : IRateLimiterPolicy<string> { // 可以通过依赖注入参数 public MyRateLimiterPolicy(ILogger<MyRateLimiterPolicy> logger) { // 可以设置自己的限流拒绝回调逻辑,而不使用上面全局设置的 limiterOptions.OnRejected OnRejected = (ctx, token) => { ctx.HttpContext.Response.StatusCode = StatusCodes.Status429TooManyRequests; logger.LogWarning($"Request rejected by {nameof(MyRateLimiterPolicy)}"); return ValueTask.CompletedTask; }; } public Func<OnRejectedContext, CancellationToken, ValueTask>? OnRejected { get; } public RateLimitPartition<string> GetPartition(HttpContext httpContext) { var userId = "anonymous user"; if (httpContext.User.Identity?.IsAuthenticated is true) { userId = httpContext.User.Claims.First(c => c.Type == "id").Value; } return RateLimitPartition.GetFixedWindowLimiter(partitionKey: userId, _ => new FixedWindowRateLimiterOptions { PermitLimit = 3, Window = TimeSpan.FromSeconds(60), QueueProcessingOrder = QueueProcessingOrder.OldestFirst, QueueLimit = 0 }); } } // 记得注册它 builder.Services.AddRateLimiter(limiterOptions => { limiterOptions.AddPolicy<string, MyRateLimiterPolicy>(policyName: "my_policy"); } 限流应用

EnableRateLimitingAttribute & DisableRateLimitingAttribute

标签:

###net7+出现了自带的限流中间件固定窗口、滑动窗口并发令牌桶全局限流器由讯客互联创业栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“###net7+出现了自带的限流中间件固定窗口、滑动窗口并发令牌桶全局限流器