asp.net core服务中的限流
使用了AspNetCoreRateLimit三方库,starup.cs配置如下。
using AspNetCoreRateLimit;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
namespace RateLimitDemo01
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
// needed to load configuration from appsettings.json
services.AddOptions();
// needed to store rate limit counters and ip rules
services.AddMemoryCache();
//load general configuration from appsettings.json
services.Configure<IpRateLimitOptions>(Configuration.GetSection("IpRateLimiting"));
//load ip rules from appsettings.json
services.Configure<IpRateLimitPolicies>(Configuration.GetSection("IpRateLimitPolicies"));
// inject counter and rules stores
services.AddSingleton<IIpPolicyStore, MemoryCacheIpPolicyStore>();
services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>();
// the IHttpContextAccessor service is not registered by default.
// the clientId/clientIp resolvers use it.
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// configuration (resolvers, counter key builders)
services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "RateLimitDemo01", Version = "v1" });
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "RateLimitDemo01 v1"));
}
app.UseIpRateLimiting();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
客户端用了五组请求:None白名单 ,无限制,WhiteIP1限流1秒2次请求 ,WhiteIP2限流,因为每秒一次请求,所以分在一分钟五次的限流组中,ClientID001允许的客户ID,ClientID002不允许的客户端ID。
using System;
using System.Net.Http;
using System.Threading.Tasks;
namespace RateLimitDemo01_Client
{
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("");
Console.WriteLine("回车开始:");
Console.ReadLine();
var url = "https://localhost:5001";
Console.WriteLine("---------None-------------");
await None(url);
Console.WriteLine("---------WhiteIP1-------------");
await WhiteIP1(url);
System.Threading.Thread.Sleep(2000);
Console.WriteLine("---------WhiteIP2-------------");
await WhiteIP2(url);
Console.WriteLine("---------ClientID001-------------");
await ClientID001(url);
Console.WriteLine("---------ClientID002-------------");
await ClientID002(url);
Console.ReadLine();
}
static async Task None(string url)
{
for (var i = 0; i < 5; i++)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(url);
//appsettings中配置,所以自由访问 "EndpointWhitelist": [ "get:/none", "*:/home/add" ],
var request = new HttpRequestMessage(HttpMethod.Get, "/none");
var response = await client.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine($"状态码:{response.StatusCode},{(int)response.StatusCode},返回值:" + content);
}
}
}
static async Task WhiteIP1(string url)
{
for (var i = 0; i < 5; i++)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(url);
/*"
GeneralRules": [
{
"Endpoint": "*",
"Period": "1s",
"Limit": 2
}
……
*/
var request = new HttpRequestMessage(HttpMethod.Get, "/whiteip1");
var response = await client.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine($"状态码:{response.StatusCode},{(int)response.StatusCode},返回值:" + content);
}
}
}
static async Task WhiteIP2(string url)
{
for (var i = 0; i < 5; i++)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(url);
/*"GeneralRules": [
……
{
"Endpoint": "*",
"Period": "1m",
"Limit": 5
}
]*/
System.Threading.Thread.Sleep(1000);
var request = new HttpRequestMessage(HttpMethod.Get, "/whiteip2");
var response = await client.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine($"状态码:{response.StatusCode},{(int)response.StatusCode},返回值:" + content);
}
}
}
static async Task ClientID001(string url)
{
for (var i = 0; i < 5; i++)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(url);
var request = new HttpRequestMessage(HttpMethod.Get, "/clientid");
request.Headers.Add("X-ClientId", "client_level_001");
var response = await client.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine($"状态码:{response.StatusCode},{(int)response.StatusCode},返回值:" + content);
}
}
}
static async Task ClientID002(string url)
{
for (var i = 0; i < 5; i++)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(url);
var request = new HttpRequestMessage(HttpMethod.Get, "/clientid");
request.Headers.Add("X-ClientId", "client_level_002");
var response = await client.SendAsync(request);
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine($"状态码:{response.StatusCode},{(int)response.StatusCode},返回值:" + content);
}
}
}
}
}
web服务的appsettings.json配置如下:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"AllowedHosts": "*",
"IpRateLimiting": {
"EnableEndpointRateLimiting": false,
"StackBlockedRequests": false,
"RealIpHeader": "X-Real-IP",
"ClientIdHeader": "X-ClientId",
"HttpStatusCode": 429,
"IpWhitelist": [ "127.0.0.1" ],
"EndpointWhitelist": [ "get:/none", "*:/home/add" ],
"ClientWhitelist": [ "client_level_001" ],
"GeneralRules": [
{
"Endpoint": "*",
"Period": "1s",
"Limit": 2
},
{
"Endpoint": "*",
"Period": "1m",
"Limit": 5
}
]
},
"IpRateLimitPolicies": {
"IpRules": [
{
"Ip": "127.0.0.1",
"Rules": [
{
"Endpoint": "*",
"Period": "1s",
"Limit": 10
},
{
"Endpoint": "*",
"Period": "15m",
"Limit": 200
}
]
}
]
}
}
其中 EnableEndpointRateLimiting是全部请求累计还是每个API请求累计,StackBlockedRequests拒约的请求是否计入计数器中。
一场景
"IpRateLimiting": {
"EnableEndpointRateLimiting": false,
"StackBlockedRequests": false,
……
结果
None是不受限流限制的,因为在白名称内。WhiteIP1是因为一秒只能有两次请求,所以剩下的三次拒绝了,WhiteIP2是等待了两秒杀后再次一秒一次请求,所以请求了三次,第四次出错了,是因为一分钟只能有五次请求。ClientID001是允许的客户端,五次全过,ClientID002是不允许的客户端,五次全部拒绝。
二场景
"IpRateLimiting": {
"EnableEndpointRateLimiting": true,
"StackBlockedRequests": false,
……
当EnalbeEndopintRateLimiting为true时,第个url都是独立计算的,WhiteI2因为是新的请求,每秒一次,所以全部通过,ClientID002虽然ClientID不正确,但是一秒两次的限制生效了。
三场景
"IpRateLimiting": {
"EnableEndpointRateLimiting": false,
"StackBlockedRequests": true,
……
StackBlockedRequests为true,拒绝的请也会算在计数中,所以超过了每分钟五次的限制,WhiteIP2会被拒绝,如果把每次钟改成六次,可以看一下效果。
四场景
"IpRateLimiting": {
"EnableEndpointRateLimiting": true,
"StackBlockedRequests": true,
……
与场景二相同,每个api单独计数,失败的也算在内。
想要更快更方便的了解相关知识,可以关注微信公众号
****欢迎关注我的asp.net core系统课程****
《asp.net core精要讲解》 https://ke.qq.com/course/265696
《asp.net core 3.0》 https://ke.qq.com/course/437517
《asp.net core项目实战》 https://ke.qq.com/course/291868
《基于.net core微服务》 https://ke.qq.com/course/299524
《asp.net core精要讲解》 https://ke.qq.com/course/265696
《asp.net core 3.0》 https://ke.qq.com/course/437517
《asp.net core项目实战》 https://ke.qq.com/course/291868
《基于.net core微服务》 https://ke.qq.com/course/299524