限流是对外Api服务在使用过程上经常会碰到的需求。
对客户端的访问频率进行限制可以有效防止因为客户端使用脚本或其他破坏性的方式对服务正常运行造成影响的风险。
限流有多种解决方式,最简单的方式莫过于针对Ip进行限制:只允许某一个Ip在规定的时间内访问多次,ip访问记录可以保存在内存或者其他高速数据存储服务中。
当然我们在开发过种中肯定不会什么轮子都自己造,既浪费时间,出错概率大,考虑不全面。所以选择一个合适的轮子是非常重要的,今天在这里向大家推荐一个ASP.NET Core速率限制的解决方案**AspNetCoreRateLimit **
AspNetCoreRateLimit 介绍
AspNetCoreRateLimit是一个ASP.NET Core速率限制的解决方案,旨在控制客户端根据IP地址或客户端ID向Web API或MVC应用发出的请求的速率。AspNetCoreRateLimit包含一个IpRateLimitMiddleware和ClientRateLimitMiddleware,每个中间件可以根据不同的场景配置限制允许IP或客户端,自定义这些限制策略,也可以将限制策略应用在每个API URL或具体的HTTP Method上。
Nuget包
Nuget包引用,截止文章使用时 AspNetCoreRateLimit版本号3.2.2。
代码语言:javascript复制Install-Package AspNetCoreRateLimit
配置说明
通用Ip配置规则
IpRateLimiting
名称 | 类型 | 说明 |
---|---|---|
EnableEndpointRateLimiting | bool | false 则全局将应用限制,并且仅应用具有作为端点的规则*。例如,如果您设置每秒5次调用的限制,则对任何端点的任何HTTP调用都将计入该限制<br/>true 则限制将应用于每个端点,如{HTTP_Verb}{PATH}。例如,如果您为 *:/api/values客户端设置每秒5个呼叫的限制 |
StackBlockedRequests | bool | true 如果希望被拒绝的API调用计入其他时间的显示(分钟,小时等)<br />false 拒绝的API调用不会添加到调用次数计数器上;如客户端每秒发出3个请求并且您设置了每秒一个调用的限制,则每分钟或每天计数器等其他限制将仅记录第一个调用,即成功的API调用 |
RealIpHeader | string | 服务器背后是一个反向代理,如果你的代理服务器使用不同的页眉然后提取客户端IP X-Real-IP使用此选项来设置 |
ClientIdHeader | string | 取白名单的客户端ID。如果此标头中存在客户端ID并且与ClientWhitelist中指定的值匹配,则不应用速率限制。 |
HttpStatusCode | string | 限制状态码 |
IpWhitelist | string | IP白名单:支持Ip v4和v6 |
EndpointWhitelist | string | 端点白名单 |
ClientWhitelist | string | 白客户端白名单 |
GeneralRules | json | 通用规则 |
QuotaExceededResponse | json | 自定义返回的内容<br />"QuotaExceededResponse": {<br/> "Content": "{{"code":429,"msg":"访问过于频繁,请稍后重试","data":null}}",<br/> "ContentType": "application/json",<br/> "StatusCode": 200<br/>} |
参考内容,下面的配置文件代表的含义是:IP限制适应于所有全局,规则为每5秒访问3次
代码语言:javascript复制{
"IpRateLimiting": {
"EnableEndpointRateLimiting": false,
"StackBlockedRequests": false,
"RealIpHeader": "X-Real-IP",
"ClientIdHeader": "X-ClientId",
"HttpStatusCode": 429,
////IP白名单:支持Ip v4和v6
"IpWhitelist": [ "127.0.0.1" ],
"GeneralRules": [
{
"Endpoint": "*",
"Period": "5s",
"Limit": 3
}
]
}
}
GeneralRules
名称 | 类型 | 说明 |
---|---|---|
Endpoint | string | 端点路径 |
Period | string | 时间段,格式:{数字}{单位};可使用单位:s, m, h, d |
Limit | string | 调用限制 |
EndPoint
端点格式:{HTTP_Verb}:{PATH}
,您可以使用asterix符号来定位任何HTTP谓词。
Period
期间格式:{INT}{PERIOD_TYPE}
,您可以使用以下期间类型之一:s, m, h, d
。
Limit
限制格式:{LONG}
配置规则参考
代码语言:javascript复制"GeneralRules": [
//1秒钟只能访问2次
{
"Endpoint": "*",
"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
},
//对api/rateLimit/GetDateTime接口的规则为5秒钟最多访问3次
{
"Endpoint": "*:/api/rateLimit/GetDateTime",
"Period": "5s",
"Limit": 3
}
]
特殊Ip限制规则
代码语言:javascript复制"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}
]
}
]
}
相信看完通用Ip配置规则的小伙伴看这一段配置应该没有什么压力了,我在此也就不多做解释了。
使用特殊Ip限制规则需要在ConfigureService中启用这一段代码
代码语言:javascript复制//加载ip限制定制策略,即针对某一个ip的特殊限制,可选择性注释
//services.Configure<IpRateLimitPolicies>(configuration.GetSection("IpRateLimitPolicies"));
编码使用
了解了基本规则后,我们开始进行编码工作
1、注入服务
因为涉及的注入内容比较多,我们使用一个扩展方法标识Ip限制服务需要注入的内容。
代码语言:javascript复制public static class RateLimitedConfigureService
{
public static void RateLimitedConfig(this IServiceCollection services, IConfiguration configuration)
{
//Option模式配置
services.AddOptions();
//存储速率限制计算器和ip规则
services.AddMemoryCache();
//加载ip限制通用策略
services.Configure<IpRateLimitOptions>(configuration.GetSection("IpRateLimiting"));
//加载ip限制定制策略,即针对某一个ip的特殊限制,可选择性注释
//services.Configure<IpRateLimitPolicies>(configuration.GetSection("IpRateLimitPolicies"));
//注入计数器和规则存储
services.AddSingleton<IIpPolicyStore, MemoryCacheIpPolicyStore>();
services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>();
//httpContext使用需要注入的服务
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
//配置(解析器、计数器密钥生成器)
services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
}
}
1.1 配置服务
代码语言:javascript复制public void ConfigureServices(IServiceCollection services)
{
//Ip限制配置
services.RateLimitedConfig(_configuration);
services.AddControllers();
}
1.2 启用管道
代码语言:javascript复制public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
//管道事件启用客户端IP限制速率
app.UseIpRateLimiting();
}
2、编写Api
代码语言:javascript复制/// <summary>
/// 流量限制使用说明
/// </summary>
[Route("api/rateLimit")]
[ApiController]
public class RateLimitController: Controller
{
[HttpGet("GetDateTime")]
public string GetDateTime()
{
return DateTime.Now.ToString("d");
}
}
3、测试
正常请求
调用 http://localhost:5000/api/ratelimit/getdatetime请求接口,返回当前日期。
通过F12查看ResponseHeaders我们可以发现多了三个参数,分别代表的含义为
X-Rate-Limit-Limit 限制时间区间
X-Rate-Limit-Remaining 剩余请求次数
X-Rate-Limit-Reset 请求重置时间
每调用一次X-Rate-Limit-Remaining
减去1,当没有请求次数可以调用时返回错误。
API calls quota exceeded! maximum admitted 3 per 5s.
错误请求
当请求数在时间界限外时页面返回错误信息。返回内容也可以自定义,请参考配置规则
参考
Asp.NET Core 限流控制-AspNetCoreRateLimit
.Net Core结合AspNetCoreRateLimit实现限流