使用了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单独计数,失败的也算在内。
想要更快更方便的了解相关知识,可以关注微信公众号