当前位置: 首页 > news >正文

### 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

在这里插入图片描述

相关文章:

  • deepseek本地部署方案(超简单)
  • 网页制作02-html,css,javascript初认识のhtml的文字与段落标记
  • Audio-Visual Speech Enhancement(视听语音增强)领域近三年研究进展与国内团队及手机厂商动态分析
  • js闭包,跨域
  • Effective C++读书笔记——item49(了解new-handle的行为)
  • 深度学习:从技术突破到未来展望
  • Linux系统 -- 环境安装,xshell和多用户,基本的Linux指令和Linux的用处
  • OpenCV中的边缘检测
  • 从低清到4K的魔法:FlashVideo突破高分辨率视频生成计算瓶颈(港大港中文字节)
  • Tomcat如何处理Http请求
  • 白话概念模型、逻辑模型与物理模型
  • ubuntu 安装 Redis
  • Java和JavaScript当中的json对象和json字符串分别讲解
  • Weather Regimes(WRs)方法介绍
  • 股指期货是什么?股指期货日内拐点有什么特征?
  • 备战蓝桥杯:贪心算法之货仓选址
  • 存储引擎---数据库
  • spring的核心配置
  • 什么是DNS?DNS解析的过程是怎样的?
  • Zookeeper分布式锁实现
  • 调查:“网约摩的”上线起步价五六元,合规性及安全性引质疑
  • 上海将建设万兆小区、园区及工厂,为模型训练数据的传输提供硬件支持
  • 第十届曹禺剧本奖上海揭晓,首次开放个人申报渠道
  • 一箭六星,朱雀二号改进型遥二运载火箭发射成功
  • 湖南4个县市区被确定为野生蘑菇中毒高风险区:中毒尚无特效解毒药
  • 精品消费“精”在哪?多在体验上下功夫