Asp.NET Core 限流控制-AspNetCoreRateLimit
起因:
近期项目中,提供了一些调用频率较高的api接口,需要保障服务器的稳定运行;需要对提供的接口进行限流控制。避免因客户端频繁的请求导致服务器的压力。
一、AspNetCoreRateLimit 介绍
AspNetCoreRateLimit是一个ASP.NET Core速率限制的解决方案,旨在控制客户端根据IP地址或客户端ID向Web API或MVC应用发出的请求的速率。AspNetCoreRateLimit包含一个IpRateLimitMiddleware和ClientRateLimitMiddleware,每个中间件可以根据不同的场景配置限制允许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
.NET Core实战项目之CMS 第一章 入门篇-开篇及总体规划
【.NET Core微服务实战-统一身份认证】开篇及目录索引
Redis基本使用及百亿数据量中的使用技巧分享(附视频地址及观看指南)
.NET Core中的一个接口多种实现的依赖注入与动态选择看这篇就够了
用abp vNext快速开发Quartz.NET定时任务管理界面