一、应用场景
微服务架构中,限流功能一般由网关提供。而对于很多非微服务化的系统,可能并没有网关[无论是因为成本还是复杂度],在这种场景下,为了实现限流,.NET 7中提供了限流中间件 Rate Liniting。
二、实现
首先,SDK版本 >= 7。
然后添加代码注册。
微软为我们提供了4中常用的限流算法:
- 固定窗口限流器【FixedWindowLimiter】
- 滑动窗口限流器【SlidingWindowLimiter】
- 令牌桶限流器【TokenBucketLimiter】
- 并发限流器【ConcurrencyLimiter】
通常我们会注册一个命名限流策略,并在该策略内指定限流算法,以及其他限流逻辑。需要注意的是,UseRateLimiter的位置,若限流行为作用于特定路由,这限流中间件必须放到UseRouting之后。
三、详情
3.1、固定窗口限流器
固定窗口限流器比较简单,限流方式如下:
【原理】:使用固定的时间长度来限制请求数量。如固定窗口长度为10s,则每10s就会切换(销毁并创建)一个新窗口,在每个单独的窗口内,限制请求数量。
【特点】:
优点:实现简单,内存占用低。
缺点:
1、当窗口QPS到达阈值,流量会被瞬间切断,不能平滑处理突发流量(实际应用中理想效果是让流量平滑的进入系统)
2、窗口切换时可能出现2倍QPS。如窗口大小为1s,阈值为100,窗口1在后500ms内处理100个请求,窗口2在前500ms内也处理100个请求,这样就导致实际在1s内处理了200个请求。
#region 注册限流中间件 builder.Services.AddRateLimiter(options => { //1、固定窗口限流策略 //配置说明:该固定窗口60s时间内,可以最多有100+50=150个请求,100个会被处理,50个会被排队,其他则会在一定时间后拒绝返回 RejectionStatusCode options.AddFixedWindowLimiter(policyName:"fixed",fixedOptions => { fixedOptions.PermitLimit = 100;//每个窗口时间范围内,允许100个请求被处理 fixedOptions.Window = TimeSpan.FromSeconds(60); //窗口大小。即窗口时间长度60s。必须>TimeSpan.Zero fixedOptions.QueueLimit = 50;//窗口阈值。即每个窗口时间范围内,最多允许的请求个数。该值必须>0。当窗口请求达到最大值,后续请求会进入排队。该值用于设置对垒大小(即允许几个请求在排队队列等待) fixedOptions.QueueProcessingOrder = System.Threading.RateLimiting.QueueProcessingOrder.OldestFirst;//排队请求的处理顺序。这里设置为有限处理先来的请求 fixedOptions.AutoReplenishment = true;//开启新窗口时是否自动重置请求限制,默认true。如果是false,则需要手动调佣 FixedWindowRateLimiter.TryReplenish来重置 }); }); #endregion //使用限流器 app.UseRateLimiter();View Code
3.2、滑动窗口限流器
滑动窗口限流器是固定窗口限流器的升级版。
【原理】:在固定窗口限流器的基础上,它将每个窗口划分为多个段,每经过一个段的时间间隔(=窗口时间/窗口段的个数),窗口就会向后滑动一段,所以称为滑动窗口(窗口大小仍是固定的)。当窗口滑动后,会“吃进”一个段(称为当前段),并“吐出”一个段(称为过期端),过期段会被回收,回收的请求数可以用于当前段。
【特点】:
优点:按段滑动处理,相对于固定窗口,可以对流量进行更精准的控制,更平滑的处理突发流量,并且段划分的越多,移动更平滑。
缺点:对时间精度要求高,比固定窗口实现复杂,占用内存更高。
//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; });View Code
【理解】:滑动窗口就是为了解决固定窗口2n的问题。
计算公式:估计数 = 前一窗口技术 * (1 - 当前窗口经过时间/单位时间)+ 当前窗口计数
举个例子:窗口为每分钟最大处理10个请求。这实际计算后数据为:估计数=9 * (1-25%) + 5 = 11.75 > 10,则最后的一次请求会被阻止。虽然在单个窗口内,请求数都没有超过最大限制,但是超过了加权计算结果。
另外,滑动窗口的段,可以理解为每次加权计算移动的时间距离。如果最近的2次请求相距两个时间窗口,则可以认为前一窗口计数为零,重新开始计数。
3.3、令牌桶限流器
令牌桶限流器是一种限制数据平均传输速率的限流算法。
【原理】:想象有一个桶,每个固定时间段会向桶内放入固定数量的令牌(token),当桶内令牌装满时,新的令牌将会被丢弃。当请求流量进入时,会先从桶内拿 1 个令牌,拿到了则该请求会被处理,没拿到则会在队列中等待,若队列已满,则会被限流拒绝处理。
【特点】:可以限制数据的平均传输速率,还可以一次性耗尽令牌应对突发流量,并平滑地处理后续流量,是一种通用的算法。
//策略说明:桶最多装4个令牌,每10秒发放一次令牌,每次发放2个令牌,所以在一个发放周期,最多可以处理4个请求,至少可以处理2个请求。 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来发放 });View Code
3.4、并发限流器
并发限流器不是限制一段时间内的最大请求数,而是限制并发数。
【原理】:限制同一时刻并发请求的数量。
【特点】:可以充分利用服务器的性能,当出现突发流量时,服务器负载可能会持续过高。
//策略说明:最大并发请求4,超过最大并发请求,则后续最多2个请求进入排队队列。 options.AddConcurrencyLimiter(policyName:"concurrency", concurrencyOptions => { concurrencyOptions.PermitLimit = 4;//最大并发请求数 concurrencyOptions.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; concurrencyOptions.QueueLimit = 2;//当并发请求数达到最大,后续请求进入排队,该参数用于配置队列大小 });View Code
标签:Core,令牌,ASP,窗口,请求,限流,滑动,100 From: https://www.cnblogs.com/xiaobaicai12138/p/17918987.html