广告
返回顶部
首页 > 资讯 > 精选 >.Net Core如何实现限流
  • 191
分享到

.Net Core如何实现限流

2023-06-20 13:06:52 191人浏览 八月长安
摘要

小编给大家分享一下.net core如何实现限流,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!一、环境vs2019.Net Core 3.1引用 Aspnetco

小编给大家分享一下.net core如何实现限流,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

    一、环境

    vs2019

    .Net Core 3.1

    引用 AspnetcoreRateLimit 4.0.1

    二、基础使用

    1.设置

    在Startup文件中配置如下,把配置项都放在前面:

     public void ConfigureServices(IServiceCollection services) {  // 从appsettings.JSON中加载ip限流配置通用规则  services.Configure<IpRateLimitOptions>(Configuration.GetSection("IpRateLimiting"));  // 从appsettings.json中加载ip限流规则  services.Configure<IpRateLimitPolicies>(Configuration.GetSection("IpRateLimiting:IpRateLimitPolicies"));  // 从appsettings.json中加载客户端限流配置通用规则  services.Configure<ClientRateLimitOptions>(Configuration.GetSection("IpRateLimiting"));  // 从appsettings.json中加载客户端限流规则  services.Configure<ClientRateLimitPolicies>(Configuration.GetSection("IpRateLimiting:ClientRateLimitPolicies"));  // 注入计数器和规则存储  services.AddInMemoryRateLimiting();  // 配置(解析器、计数器密钥生成器)  services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();  //解析clientid和ip的使用有用,如果默认没有启用,则此处启用  //services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();}public void Configure(IApplicationBuilder app, IHostingEnvironment env){  //调用ip限流方式和客户端限流方式  //只能选用一个,后一个调用的生效,也就是说ip规则限流和客户端限流的特殊规则不能同时使用,但是通用规则不影响  app.UseIpRateLimiting();  app.UseClientRateLimiting();}

    2.规则设置

    规则的设置分为两个大类:通过IP限流和通过客户端限流。都通过配置文件来配置参数,在appsettings.json中配置如下(也可以另起配置文件):

    "IpRateLimiting": {    "EnableEndpointRateLimiting": false,    "StackBlockedRequests": false,    "RealIpHeader": "X-Real-IP",    "ClientIdHeader": "X-ClientId",    "httpstatusCode": 429,    //"IpWhitelist": [ "198.0.0.1", "::1/10", "192.168.0.13/24" ],    "EndpointWhitelist": [ "get:/api/license", "*:/api/status" ],    "ClientWhitelist": [ "dev-id-1", "dev-id-2" ],    "QuotaExceededResponse": {      "Content": "{{\"code\":429,\"msg\":\"Visit too frequently, please try again later\",\"data\":null}}",      "ContentType": "application/json;utf-8",      "StatusCode": 429    },    "GeneralRules": [      {        "Endpoint": "*",        "Period": "1s",        "Limit": 2      }    ],    "ClientRateLimitPolicies": {      "ClientRules": [        {          "ClientId": "client-id-1",          "Rules": [            {              "Endpoint": "*",              "Period": "1s",              "Limit": 10            },            {              "Endpoint": "*",              "Period": "15m",              "Limit": 200            }          ]        }      ]    },    "IpRateLimitPolicies": {      "IpRules": [        {          "Ip": "84.247.85.224",          "Rules": [            {              "Endpoint": "*",              "Period": "1s",              "Limit": 10            },            {              "Endpoint": "*",              "Period": "15m",              "Limit": 200            }          ]        }      ]    }  }

    各配置项的说明如下:

     EnableEndpointRateLimiting:设置为true,则端点规则为 * 的时候所有的谓词如GET、POST等分别享有限制次数。例如,如果您为*:/api/values客户端设置每秒GET /api/values5 次调用的限制,则每秒可以调用5 次,但也可以调用5 次PUT /api/values。

    如果设置为false,则上述例子中GET、POST等请求共享次数限制。是否共享限制次数的设置。这里有个注意的地方,就是当该参数设置为false的时候,只有端点设置为星号*的规则有效,其他规则无效,设置为true时所有规则有效。

    StackBlockedRequests:设为false的情况下,被拒绝的请求不会加入到计数器中,如一秒内有三个请求,限流规则分别为一秒一次和一分钟三次,则被拒绝的两个请求是不会记录在一分钟三次的规则中的,也就是说这一分钟还能调用两次该接口。设置为true的话,则被拒绝的请求也会加入计数器,像上述例子中的情况,一分钟内就不能调用了,三次全部记录了。

    RealIpHeader:与配置项IP白名单IpWhitelist组合使用,如果该参数定义的请求头名称存在于一个请求中,并且该参数内容为IP白名单中的IP,则不受限流规则限制。

    ClientIdHeader:与配置项客户端白名单ClientIdHeader组合使用,如果该参数定义的请求头名称存在于一个请求中,并且该参数内容为客户端白名单中的名称,则不受限流规则限制。

    HttpStatusCode:http请求限流后的返回码。

    IpWhitelist:IP白名单,字段支持支持Ip v4和v6如 "198.0.0.1", "::1/10", "192.168.0.13/24"等。可以配合RealIpHeader参数使用,也单独使用,请求的ip符合该白名单规则任意一条,则不受限流规则限制。

    EndpointWhitelist:终端白名单,符合该终端规则的请求都将不受限流规则影响,如"get:/api/values"表示GET请求的api/values接口不受影响,*表示所有类型的请求。

    ClientWhitelist:客户端白名单,配合ClientIdHeader参数使用,配置客户端的名称。

    QuotaExceededResponse:限流后的返回值设置,返回内容、状态码等。

    GeneralRules:通用规则设置,有三个参数为Endpoint、Period和Limit。

    Endpoint端点格式为{HTTP_Verb}:{PATH},可以使用星号来定位任何 HTTP 动词,如get:/api/values。

    Period期间格式为{INT}{PERIOD_TYPE},可以使用以下期间类型之一:s、m、h、d,分别为秒分时天。

    Limit限制格式为{LONG},访问次数。

    ClientRateLimitPolicies:客户端限流的特殊配置,规则和通用规则一样设置,只不过需要配合ClientIdHeader在请求头中来使用,需要使用app.UseClientRateLimiting();启用,否则无效。这个参数名称是可以更改的噢。通用规则和特殊规则是同优先级的。

    IpRateLimitPolicies:IP限流的特殊配置,规则和通用规则一样设置,只不过需要配合RealIpHeader在请求头中来使用,需要使用app.UseIpRateLimiting();启用,否则无效。这个参数名称是可以更改的噢。通用规则和特殊规则是同优先级的。

    3.特殊规则的启用

    IP和客户端特殊规则的启用需要改造Program文件中的程序入口如下,分别发送各自的特殊规则:

    public static async Task Main(string[] args){  IWEBHost webHost = CreateWebHostBuilder(args).Build();  using (var scope = webHost.Services.CreateScope())  {    var clientPolicyStore = scope.ServiceProvider.GetRequiredService<IClientPolicyStore>();    await clientPolicyStore.SeedAsync();    var ipPolicyStore = scope.ServiceProvider.GetRequiredService<IIpPolicyStore>();    await ipPolicyStore.SeedAsync();  }  await webHost.RunAsync();}

    在ConfigureServices中读取配置参数,之后是在Startup文件中的Configure方法选择app.UseIpRateLimiting()或app.UseClientRateLimiting()启动IP特殊规则或者客户端特殊规则,都存在的情况下,先执行的先生效。

     三、请求返回头

    限流启动后,执行限流规则的返回头会有三个参数分别为:

    X-Rate-Limit-Limit:现在时间,如1d。

    X-Rate-Limit-Remaining:剩余可请求次数。

    X-Rate-Limit-Reset:下次请求次数重置时间。

    多个限制规则会采用最长的周期的规则显示。

    在配置文件中配置返回信息,除了返回提示信息外,还可以返回限制规则提醒,如下

    "Content": "{{\"code\":429,\"msg\":\"访问太频繁了,每{1}{0}次,请在{2}秒后重试\",\"data\":null}}",

    {0}可以替换当前阻止规则规定的次数,{1}可以替换时间区间带单位s、h等,{2}替换几秒后尝试当单位为天或者小时等都会换算成秒。

    四、使用Redis存储

    限流规则等目前都是通过内存存储的,我们结合实际会使用Redis存储。使用Microsoft.Extensions.Caching.Redis可以达到这么目的。

    但是好像会存在性能问题,所以我们自己替换,使用的是用CSRedis封装的方法,不过这里不做阐述。

    我们缓存三类数据1、访问计数2、ip特殊规则3、客户端特殊规则

    1、访问计数

    public class RedisRateLimitCounterStore : IRateLimitCounterStore    {        private readonly ILogger _logger;        private readonly IRateLimitCounterStore _memoryCacheStore;        private readonly RedisCache _redisCache;        public RedisRateLimitCounterStore(            IMemoryCache memoryCache,            ILogger<RedisRateLimitCounterStore> logger)        {            _logger = logger;            _memoryCacheStore = new MemoryCacheRateLimitCounterStore(memoryCache);            _redisCache = new RedisCache();        }        public async Task<bool> ExistsAsync(string id, CancellationToken cancellationToken = default)        {            cancellationToken.ThrowIfCancellationRequested();            return await TryRedisCommandAsync(                () =>                {                    return _redisCache.KeyExistsAsync(id, 0);                },                () =>                {                    return _memoryCacheStore.ExistsAsync(id, cancellationToken);                });        }        public async Task<RateLimitCounter?> GetAsync(string id, CancellationToken cancellationToken = default)        {            cancellationToken.ThrowIfCancellationRequested();            return await TryRedisCommandAsync(                async () =>                {                    var value = await _redisCache.GetStringAsync(id, 0);                    if (!string.IsNullOrEmpty(value))                    {                        return JsonConvert.DeserializeObject<RateLimitCounter?>(value);                    }                    return null;                },                () =>                {                    return _memoryCacheStore.GetAsync(id, cancellationToken);                });        }        public async Task RemoveAsync(string id, CancellationToken cancellationToken = default)        {            cancellationToken.ThrowIfCancellationRequested();            _ = await TryRedisCommandAsync(                async () =>                {                    await _redisCache.KeyDeleteAsync(id, 0);                    return true;                },                async () =>                {                    await _memoryCacheStore.RemoveAsync(id, cancellationToken);                    return true;                });        }        public async Task SetAsync(string id, RateLimitCounter? entry, TimeSpan? expirationTime = null, CancellationToken cancellationToken = default)        {            cancellationToken.ThrowIfCancellationRequested();            _ = await TryRedisCommandAsync(                async () =>                {                    var exprie = expirationTime.HasValue ? Convert.ToInt32(expirationTime.Value.TotalSeconds) : -1;                    await _redisCache.SetStringAsync(id, JsonConvert.SerializeObject(entry.Value), exprie);                    return true;                },                async () =>                {                    await _memoryCacheStore.SetAsync(id, entry, expirationTime, cancellationToken);                    return true;                });        }        private async Task<T> TryRedisCommandAsync<T>(Func<Task<T>> command, Func<Task<T>> fallbackCommand)        {            if (_redisCache != null)            {                try                {                    return await command();                }                catch (Exception ex)                {                    _logger.LogError($"Redis command failed: {ex}");                }            }            return await fallbackCommand();        }    }

     2、ip特殊规则

    public class RedisIpPolicyStore : IIpPolicyStore    {        private readonly IpRateLimitOptions _options;        private readonly IpRateLimitPolicies _policies;        private readonly RedisCache _redisCache;        public RedisIpPolicyStore(            IOptions<IpRateLimitOptions> options = null,            IOptions<IpRateLimitPolicies> policies = null)        {            _options = options?.Value;            _policies = policies?.Value;            _redisCache = new RedisCache();        }        public async Task<bool> ExistsAsync(string id, CancellationToken cancellationToken = default)        {            return await _redisCache.KeyExistsAsync($"{_options.IpPolicyPrefix}", 0);        }        public async Task<IpRateLimitPolicies> GetAsync(string id, CancellationToken cancellationToken = default)        {            string stored = await _redisCache.GetStringAsync($"{_options.IpPolicyPrefix}", 0);            if (!string.IsNullOrEmpty(stored))            {                return JsonConvert.DeserializeObject<IpRateLimitPolicies>(stored);            }            return default;        }        public async Task RemoveAsync(string id, CancellationToken cancellationToken = default)        {            await _redisCache.DelStringAsync($"{_options.IpPolicyPrefix}", 0);        }        public async Task SeedAsync()        {            // on startup, save the IP rules defined in appsettings            if (_options != null && _policies != null)            {                await _redisCache.SetStringAsync($"{_options.IpPolicyPrefix}", JsonConvert.SerializeObject(_policies), 0).ConfigureAwait(false);            }        }        public async Task SetAsync(string id, IpRateLimitPolicies entry, TimeSpan? expirationTime = null, CancellationToken cancellationToken = default)        {            var exprie = expirationTime.HasValue ? Convert.ToInt32(expirationTime.Value.TotalSeconds) : -1;            await _redisCache.SetStringAsync($"{_options.IpPolicyPrefix}", JsonConvert.SerializeObject(_policies), 0, exprie);        }    }

    3、客户端特殊规则

    public class RedisClientPolicyStore : IClientPolicyStore    {        private readonly ClientRateLimitOptions _options;        private readonly ClientRateLimitPolicies _policies;        private readonly RedisCache _redisCache;        public RedisClientPolicyStore(            IOptions<ClientRateLimitOptions> options = null,            IOptions<ClientRateLimitPolicies> policies = null)        {            _options = options?.Value;            _policies = policies?.Value;            _redisCache = new RedisCache();        }        public async Task<bool> ExistsAsync(string id, CancellationToken cancellationToken = default)        {            return await _redisCache.KeyExistsAsync($"{_options.ClientPolicyPrefix}", 0);        }        public async Task<ClientRateLimitPolicy> GetAsync(string id, CancellationToken cancellationToken = default)        {            string stored = await _redisCache.GetStringAsync($"{_options.ClientPolicyPrefix}", 0);            if (!string.IsNullOrEmpty(stored))            {                return JsonConvert.DeserializeObject<ClientRateLimitPolicy>(stored);            }            return default;        }        public async Task RemoveAsync(string id, CancellationToken cancellationToken = default)        {            await _redisCache.DelStringAsync($"{_options.ClientPolicyPrefix}", 0);        }        public async Task SeedAsync()        {            // on startup, save the IP rules defined in appsettings            if (_options != null && _policies != null)            {                await _redisCache.SetStringAsync($"{_options.ClientPolicyPrefix}", JsonConvert.SerializeObject(_policies), 0).ConfigureAwait(false);            }        }        public async Task SetAsync(string id, ClientRateLimitPolicy entry, TimeSpan? expirationTime = null, CancellationToken cancellationToken = default)        {            var exprie = expirationTime.HasValue ? Convert.ToInt32(expirationTime.Value.TotalSeconds) : -1;            await _redisCache.SetStringAsync($"{_options.ClientPolicyPrefix}", JsonConvert.SerializeObject(_policies), 0, exprie);        }    }

    之后在Startup文件中增加对应的注入

    services.AddSingleton<IRateLimitCounterStore, RedisRateLimitCounterStore>();services.AddSingleton<IIpPolicyStore, RedisIpPolicyStore>();services.AddSingleton<IClientPolicyStore, RedisClientPolicyStore>();

    之后运行就可以在redis中看到啦

    .Net Core如何实现限流

     五、修改规则

    规则只能修改IP和客户端的特殊规则,因为上一部分已经注入了改规则的对应redis增删查改的功能,所以我们可以利用这些方法重写规则,如下:

    public class ClientRateLimitController : Controller{    private readonly ClientRateLimitOptions _options;    private readonly IClientPolicyStore _clientPolicyStore;    public ClientRateLimitController(IOptions<ClientRateLimitOptions> optionsAccessor, IClientPolicyStore clientPolicyStore)    {        _options = optionsAccessor.Value;        _clientPolicyStore = clientPolicyStore;    }    [HttpGet]    public ClientRateLimitPolicy Get()    {        return _clientPolicyStore.Get($"{_options.ClientPolicyPrefix}_cl-key-1");    }    [HttpPost]    public void Post()    {        var id = $"{_options.ClientPolicyPrefix}_cl-key-1";        var clPolicy = _clientPolicyStore.Get(id);        clPolicy.Rules.Add(new RateLimitRule        {            Endpoint = "*/api/testpolicyupdate",            Period = "1h",            Limit = 100        });        _clientPolicyStore.Set(id, clPolicy);    }}

    以上是“.net Core如何实现限流”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注编程网精选频道!

    --结束END--

    本文标题: .Net Core如何实现限流

    本文链接: https://www.lsjlt.com/news/297095.html(转载时请注明来源链接)

    有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

    本篇文章演示代码以及资料文档资料下载

    下载Word文档到电脑,方便收藏和打印~

    下载Word文档
    猜你喜欢
    • .Net Core如何实现限流
      小编给大家分享一下.Net Core如何实现限流,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!一、环境vs2019.Net Core 3.1引用 AspNetCo...
      99+
      2023-06-20
    • .Net Core限流的实现示例
      目录一、环境二、基础使用1.设置2.规则设置3.特殊规则的启用 三、请求返回头四、使用Redis存储1、访问计数 2、ip特殊规则3、客户端特殊规则 五...
      99+
      2022-11-12
    • .Net Core微服务网关Ocelot超时、熔断、限流
      基本概念 超时、熔断、限流听起来好像很远,但实际上用在方方面面。很多人可能还搞不懂熔断是做什么,其实可以把熔断理解为一种防护措施。做个假设,在微服务体系下,某个下游服务响应很慢,然后...
      99+
      2022-11-12
    • Asp.NET Core 限流控制(AspNetCoreRateLimit)的实现
      起因: 近期项目中,提供了一些调用频率较高的api接口,需要保障服务器的稳定运行;需要对提供的接口进行限流控制。避免因客户端频繁的请求导致服务器的压力。 一、AspNetCoreRa...
      99+
      2022-11-11
    • .Net Core Ocelot超时、熔断、限流的概念是什么
      本篇内容介绍了“.Net Core Ocelot超时、熔断、限流的概念是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!基本概念...
      99+
      2023-06-22
    • ASP.NET Core中间件怎么实现限流
      本篇内容介绍了“ASP.NET Core中间件怎么实现限流”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、限流算法在高并发系统中...
      99+
      2023-06-29
    • ASP.NET Core中间件实现限流的代码
      目录一、限流算法1.计数器算法1.1 固定窗口算法1.2 滑动窗口算法2.令牌桶算法3.漏桶算法二、ASP.NET Core中间件实现限流1.中间件代码2.在管道中的使用一、限流算法...
      99+
      2022-11-13
    • ASP.NET Core中使用令牌桶限流的实现
      在限流时一般会限制每秒或每分钟的请求数,简单点一般会采用计数器算法,这种算法实现相对简单,也很高效,但是无法应对瞬时的突发流量。 比如限流每秒100次请求,绝大多数的时间里都不会超过...
      99+
      2022-11-12
    • .NET CORE如何鉴权
      本篇内容主要讲解“.NET CORE如何鉴权”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“.NET CORE如何鉴权”吧!基础信息1.什么是鉴权授权?鉴权是验证用户是否拥有访...
      99+
      2023-06-29
    • 实现.NET Core配置Provider之EF
      《10分钟就能学会.NET Core配置》里详细介绍了.NET Core配置的用法另外我还开源了自定义的配置ProviderEF配置Provider和Yaml配置Provider。本文先来聊聊EF配置Pro...
      99+
      2022-10-18
    • .Net Core配置Configuration具体实现
      目录核心类 构建ConfigurationBuilder IConfigurationSource ConfigurationProvider ConfigurationRoot 查...
      99+
      2022-11-12
    • .NET CORE 鉴权的实现示例
      目录基础信息1.什么是鉴权授权?2.传统的Session和Cookie3.存在的问题4.Token.NETCore中鉴权1.NETCore鉴权授权基本概念2.使用Cookie默认流程...
      99+
      2022-11-13
    • .NET Core中如何实现或使用对象池?
      目录前言池化策略 对象池的使用 指定对象池容量 在 ASP.NET Core 中使用 总结 前言 池这个概念大家都很熟悉,比如我们经常听到数据库连接池和线程池。它是一种基于使用预先分...
      99+
      2022-11-12
    • .Net Core中如何写自定义认证实现
      今天就跟大家聊聊有关.Net Core中如何写自定义认证实现,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。一、起因 最近项目中需要对项目同时支持JWT认证,以及自定义的认证...
      99+
      2023-06-28
    • .Net Core如何配置与实现自动更新
      这篇文章给大家分享的是有关.Net Core如何配置与实现自动更新的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。.Net Core 将之前Web.Config中的配置迁移到了appsettings.json文件中...
      99+
      2023-06-19
    • ASP.NET Core基于滑动窗口实现限流控制
      目录前言:二、固定窗口算法三、滑动窗口算法四、实现六、使用结论:前言: 在实际项目中,为了保障服务器的稳定运行,需要对接口的可访问频次进行限流控制,避免因客户端频繁请求导致服务器压力...
      99+
      2022-11-13
    • .NET Core中RabbitMQ使用死信队列如何实现
      本篇内容介绍了“.NET Core中RabbitMQ使用死信队列如何实现”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!在.NET ...
      99+
      2023-07-05
    • .NET Core如何实现企业微信消息推送
      这篇文章主要介绍了.NET Core如何实现企业微信消息推送的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇.NET Core如何实现企业微信消息推送文章都会有所收获,下面我们一起来看看吧。接...
      99+
      2023-07-02
    • 如何实现redis限流
      如何实现redis限流?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。redis限流的实现方式有3种,分别是:第一种:基于Redis的set...
      99+
      2022-10-18
    • redis如何实现限流
      redis实现限流的示例:使用接口实现,接口代码如下:#指定用户user_id的某个行为action_key在特定的时间内period只允许发生最多的次数max_countdef is_action_al lowed(u...
      99+
      2022-10-08
    软考高级职称资格查询
    编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
    • 官方手机版

    • 微信公众号

    • 商务合作