当前位置 博文首页 > Asp.NET Core 限流控制(AspNetCoreRateLimit)的实现

    Asp.NET Core 限流控制(AspNetCoreRateLimit)的实现

    作者:chaney1992 时间:2021-08-22 18:59

    起因:

    近期项目中,提供了一些调用频率较高的api接口,需要保障服务器的稳定运行;需要对提供的接口进行限流控制。避免因客户端频繁的请求导致服务器的压力。

    一、AspNetCoreRateLimit 介绍

    AspNetCoreRateLimit 是一个ASP.NET Core速率限制的解决方案,旨在控制客户端根据IP地址或客户端ID向Web API或MVC应用发出的请求的速率。AspNetCoreRateLimit包含一个 IpRateLimitMiddlewareClientRateLimitMiddleware ,每个中间件可以根据不同的场景配置限制允许IP或客户端,自定义这些限制策略,也可以将限制策略应用在每​​个API URL或具体的HTTP Method上。

    二、AspNetCoreRateLimit使用

    由上面介绍可知AspNetCoreRateLimit支持了两种方式:基于 客户端IP( IpRateLimitMiddleware) 和客户端ID( ClientRateLimitMiddleware )速率限制  接下来就分别说明使用方式

    添加Nuget包引用:

    Install-Package AspNetCoreRateLimit 

    基于客户端IP速率限制

    1、修改Startup.cs中方法:

    public class Startup
    {
      public Startup(IConfiguration configuration)
      {
        Configuration = configuration;
      }
    
      public IConfiguration Configuration { get; }// This method gets called by the runtime. Use this method to add services to the container.
      public void ConfigureServices(IServiceCollection services)
      {
        //需要从加载配置文件appsettings.json
        services.AddOptions();
        //需要存储速率限制计算器和ip规则
        services.AddMemoryCache();
    
        //从appsettings.json中加载常规配置,IpRateLimiting与配置文件中节点对应
        services.Configure<IpRateLimitOptions>(Configuration.GetSection("IpRateLimiting"));
    
        //从appsettings.json中加载Ip规则
        services.Configure<IpRateLimitPolicies>(Configuration.GetSection("IpRateLimitPolicies"));
    
        //注入计数器和规则存储
        services.AddSingleton<IIpPolicyStore, MemoryCacheIpPolicyStore>();
        services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>();
    
        services.AddControllers();
    
        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
        //配置(解析器、计数器密钥生成器)
        services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
    
        //Other Code
      }
    
      // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
      public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
      {
        //Other Code
    
        app.UseRouting();
    
        app.UseAuthorization();
         //启用客户端IP限制速率
        app.UseIpRateLimiting();
    
        app.UseEndpoints(endpoints =>
        {
          endpoints.MapControllers();
        });
      }
    }

    2、在appsettings.json中添加通用配置项节点:(IpRateLimiting节点与Startup中取的节点对应)

    "IpRateLimiting": {
     //false,则全局将应用限制,并且仅应用具有作为端点的规则*。例如,如果您设置每秒5次调用的限制,则对任何端点的任何HTTP调用都将计入该限制
     //true, 则限制将应用于每个端点,如{HTTP_Verb}{PATH}。例如,如果您为*:/api/values客户端设置每秒5个呼叫的限制,
     "EnableEndpointRateLimiting": false,
     //false,拒绝的API调用不会添加到调用次数计数器上;如 客户端每秒发出3个请求并且您设置了每秒一个调用的限制,则每分钟或每天计数器等其他限制将仅记录第一个调用,即成功的API调用。如果您希望被拒绝的API调用计入其他时间的显示(分钟,小时等) //,则必须设置StackBlockedRequests为true。
     "StackBlockedRequests": false,
     //Kestrel 服务器背后是一个反向代理,如果你的代理服务器使用不同的页眉然后提取客户端IP X-Real-IP使用此选项来设置
     "RealIpHeader": "X-Real-IP",
     //取白名单的客户端ID。如果此标头中存在客户端ID并且与ClientWhitelist中指定的值匹配,则不应用速率限制。
     "ClientIdHeader": "X-ClientId",
     //限制状态码
     "HttpStatusCode": 429,
     ////IP白名单:支持Ip v4和v6 
     //"IpWhitelist": [ "127.0.0.1", "::1/10", "192.168.0.0/24" ],
     ////端点白名单
     //"EndpointWhitelist": [ "get:/api/license", "*:/api/status" ],
     ////客户端白名单
     //"ClientWhitelist": [ "dev-id-1", "dev-id-2" ],
     //通用规则
     "GeneralRules": [
      {
       //端点路径
       "Endpoint": "*",
       //时间段,格式:{数字}{单位};可使用单位:s, m, h, d
       "Period": "1s",
       //限制
       "Limit": 2
      },   //15分钟只能调用100次
      {"Endpoint": "*","Period": "15m","Limit": 100},   //12H只能调用1000
      {"Endpoint": "*","Period": "12h","Limit": 1000},   //7天只能调用10000次
      {"Endpoint": "*","Period": "7d","Limit": 10000}
     ]
    }

    配置节点已添加相应注释信息。

    规则设置格式:

    端点格式: {HTTP_Verb}:{PATH} ,您可以使用asterix符号来定位任何HTTP谓词。

    期间格式: {INT}{PERIOD_TYPE} ,您可以使用以下期间类型之一: s, m, h, d

    限制格式: {LONG}

    3、特点Ip限制规则设置,在appsettings.json中添加 IP规则配置节点

    "IpRateLimitPolicies": {
     //ip规则
     "IpRules": [
      {
       //IP
       "Ip": "84.247.85.224",
       //规则内容
       "Rules": [
        //1s请求10次
        {"Endpoint": "*","Period": "1s","Limit": 10},
        //15分钟请求200次
        {"Endpoint": "*","Period": "15m","Limit": 200}
       ]
      },
      {
       //ip支持设置多个
       "Ip": "192.168.3.22/25",
       "Rules": [
        //1秒请求5次
        {"Endpoint": "*","Period": "1s","Limit": 5},
        //15分钟请求150次
        {"Endpoint": "*","Period": "15m","Limit": 150},
        //12小时请求500次
        {"Endpoint": "*","Period": "12h","Limit": 500}
       ]
      }
     ]
    }

    基于客户端ID速率限制

    1、修改Startup文件:

    public void ConfigureServices(IServiceCollection services)
    {
      //需要从加载配置文件appsettings.json
      services.AddOptions();
    
      //需要存储速率限制计算器和ip规则
      services.AddMemoryCache();
    
      //从appsettings.json中加载常规配置
      services.Configure<ClientRateLimitOptions>(Configuration.GetSection("IPRateLimiting"));
    
      //从appsettings.json中加载客户端规则
      services.Configure<ClientRateLimitPolicies>(Configuration.GetSection("ClientRateLimitPolicies"));
    
      //注入计数器和规则存储
      services.AddSingleton<IClientPolicyStore, MemoryCacheClientPolicyStore>();
      services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>();
    
      
      services.AddControllers();
    
        // https://github.com/aspnet/Hosting/issues/793
        // the IHttpContextAccessor service is not registered by default.
        //注入计数器和规则存储
        services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    
        //配置(解析器、计数器密钥生成器)
        services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
    }
    
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        //启用客户端限制
      app.UseClientRateLimiting();
    
      app.UseMvc();
    }

    2、通用配置采用IP限制相同配置,添加客户端限制配置:

    //客户端限制设置
    "ClientRateLimitPolicies": {
     "ClientRules": [
      {
       //客户端id
       "ClientId": "client-id-1",
       "Rules": [
        {"Endpoint": "*","Period": "1s","Limit": 10},
        {"Endpoint": "*","Period": "15m","Limit": 200}
       ]
      },
      {
       "ClientId": "client-id-2",
       "Rules": [
        {"Endpoint": "*","Period": "1s","Limit": 5},
        {"Endpoint": "*","Period": "15m","Limit": 150},
        {"Endpoint": "*","Period": "12h","Limit": 500}
       ]
      }
     ]
    }

    3、调用结果:

    设置规则:1s只能调用一次:首次调用

    调用第二次:自定义返回内容

    三、其他

     运行时更新速率限制

    添加 IpRateLimitController控制器:

    /// <summary>
    /// IP限制控制器
    /// </summary>
    [Route("api/[controller]")]
    [ApiController]
    public class IpRateLimitController : ControllerBase
    {
    
      private readonly IpRateLimitOptions _options;
      private readonly IIpPolicyStore _ipPolicyStore;
    
      /// <summary>
      /// 
      /// </summary>
      /// <param name="optionsAccessor"></param>
      /// <param name="ipPolicyStore"></param>
      public IpRateLimitController(IOptions<IpRateLimitOptions> optionsAccessor, IIpPolicyStore ipPolicyStore)
      {
        _options = optionsAccessor.Value;
        _ipPolicyStore = ipPolicyStore;
      }
    
      /// <summary>
      /// 获取限制规则
      /// </summary>
      /// <returns></returns>
      [HttpGet]
      public async Task<IpRateLimitPolicies> Get()
      {
        return await _ipPolicyStore.GetAsync(_options.IpPolicyPrefix);
      }
    
      /// <summary>
      /// 
      /// </summary>
      [HttpPost]
      public async Task Post(IpRateLimitPolicy ipRate)
      {
        var pol = await _ipPolicyStore.GetAsync(_options.IpPolicyPrefix);
        if (ipRate != null)
        {
          pol.IpRules.Add(ipRate);
          await _ipPolicyStore.SetAsync(_options.IpPolicyPrefix, pol);
        }
      }
    }

    分布式部署时,需要将速率限制计算器和ip规则存储到分布式缓存中如Redis

    修改注入对象

    // inject counter and rules distributed cache stores
    services.AddSingleton<IClientPolicyStore, DistributedCacheClientPolicyStore>();
    services.AddSingleton<IRateLimitCounterStore,DistributedCacheRateLimitCounterStore>();

    添加Nuget包  Microsoft.Extensions.Caching.StackExchangeRedis 

    在Startup中设置Redis连接

    services.AddStackExchangeRedisCache(options =>
    {
      options.ConfigurationOptions = new ConfigurationOptions
      {
        //silently retry in the background if the Redis connection is temporarily down
        AbortOnConnectFail = false
      };
      options.Configuration = "localhost:6379";
      options.InstanceName = "AspNetRateLimit";
    });

    限制时自定义相应结果:

    //请求返回
      "QuotaExceededResponse": {
       "Content": "{{\"code\":429,\"msg\":\"Visit too frequently, please try again later\",\"data\":null}}",
       "ContentType": "application/json;utf-8",
       "StatusCode": 429
      },

    调用时返回结果:

    其他:

    示例代码:https://github.com/cwsheng/WebAPIVersionDemo

    jsjbwy
    下一篇:没有了