Commit e09bb559 by lcx

限流功能调整

parent e4d05297
......@@ -20,6 +20,7 @@ public static void AddAppSettingConfiguration(this IServiceCollection services,
.Configure<Application>(configuration.GetSection("Application"))
.Configure<HuyiSmsConfig>(configuration.GetSection("HuyiSmsConfig"))
.Configure<EmailOptions>(configuration.GetSection("EmailOptions"))
.Configure<RateLimitingConfig>(configuration.GetSection("RateLimitingConfig"))
.Configure<WebapiUrl>(configuration.GetSection("WebapiUrl"));
}
}
......
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Performance.Infrastructure;
using Performance.Services;
......@@ -13,6 +14,8 @@ public static void AddDependencyInjectionConfiguration(this IServiceCollection s
{
if (services == null) throw new ArgumentNullException(nameof(services));
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
#region custom util
//huyi短信发送注入
......
using AspNetCoreRateLimit;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using System;
namespace Performance.Api.Configurations
{
public static class RateLimitConfig
{
public static void AddRateLimitConfiguration(this IServiceCollection services, IConfiguration configuration)
{
if (services == null) throw new ArgumentNullException(nameof(services));
//加载配置
services.AddOptions();
//从appsettings.json获取相应配置
services.Configure<IpRateLimitOptions>(configuration.GetSection("IpRateLimiting"));
//注入计数器和规则存储
services.AddSingleton<IIpPolicyStore, MemoryCacheIpPolicyStore>();
services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
//配置(计数器密钥生成器)
services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();
}
public static void UseRateLimitSetup(this IApplicationBuilder app)
{
if (app == null) throw new ArgumentNullException(nameof(app));
app.UseIpRateLimiting();
}
}
}
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Performance.DtoModels;
using Performance.DtoModels.AppSettings;
using Performance.Infrastructure;
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
namespace Performance.Api
{
public class RequestRateLimitingMiddleware
{
private readonly int Limit;
private readonly ILogger logger;
private readonly RequestDelegate next;
private readonly IMemoryCache requestStore;
private readonly IHttpContextAccessor httpContextAccessor;
private readonly RateLimitingConfig options;
public RequestRateLimitingMiddleware(
ILogger<RequestRateLimitingMiddleware> logger,
RequestDelegate next,
IMemoryCache requestStore,
IHttpContextAccessor httpContextAccessor,
IOptions<RateLimitingConfig> options)
{
this.logger = logger;
this.next = next;
this.requestStore = requestStore;
this.httpContextAccessor = httpContextAccessor;
if (options == null)
throw new ArgumentNullException(nameof(options));
this.options = options.Value;
Limit = options.Value.Limit;
}
public async Task Invoke(HttpContext context)
{
if (options.Endpoints == null || !options.Endpoints.Any(t => context.Request.Path.ToString().StartsWith(t)))
await next(context);
var ip = httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString();
var headers = context.Request.Headers;
if (headers.ContainsKey("X-Forwarded-For"))
{
ip = IPAddress.Parse(headers["X-Forwarded-For"].ToString().Split(',', StringSplitOptions.RemoveEmptyEntries)[0]).ToString();
}
var requestKey = $"{ip}-{context.Request.Method}-{context.Request.Path}";
logger.LogInformation($"请求地址:{requestKey}");
var cacheOptions = new MemoryCacheEntryOptions()
{
AbsoluteExpiration = DateTime.Now.AddSeconds(options.Period)
};
if (requestStore.TryGetValue(requestKey, out int hitCount))
{
if (hitCount < Limit)
{
await ProcessRequest(context, requestKey, hitCount, cacheOptions);
}
else
{
// X-RateLimit-RetryAfter:超出限制后能够再次正常访问的时间。
context.Response.Headers["X-RateLimit-RetryAfter"] = cacheOptions.AbsoluteExpiration?.ToString();
context.Response.StatusCode = StatusCodes.Status200OK;
context.Response.ContentType = "application/json; charset=utf-8";
var response = new ApiResponse
{
State = ResponseType.TooManyRequests,
Message = "访问过于频繁,请稍后重试"
};
await context.Response.WriteAsync(JsonHelper.Serialize(response));
}
}
else
{
await ProcessRequest(context, requestKey, hitCount, cacheOptions);
}
}
private async Task ProcessRequest(HttpContext context, string requestKey, int hitCount, MemoryCacheEntryOptions cacheOptions)
{
hitCount++;
requestStore.Set(requestKey, hitCount, cacheOptions);
// X-RateLimit-Limit:同一个时间段所允许的请求的最大数目
context.Response.Headers["X-RateLimit-Limit"] = Limit.ToString();
// X-RateLimit-Remaining:在当前时间段内剩余的请求的数量。
context.Response.Headers["X-RateLimit-Remaining"] = (Limit - hitCount).ToString();
await next(context);
}
}
}
......@@ -36,7 +36,6 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AspNetCoreRateLimit" Version="3.0.3" />
<PackageReference Include="AutoMapper" Version="8.0.0" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="6.0.0" />
<PackageReference Include="CSRedisCore" Version="3.0.45" />
......
{
"IpRateLimiting": {
//false则全局将应用限制,并且仅应用具有作为端点的规则* true则限制将应用于每个端点,如{HTTP_Verb}{PATH}
"EnableEndpointRateLimiting": true,
//false则拒绝的API调用不会添加到调用次数计数器上
"StackBlockedRequests": false,
//注意这个配置,表示获取用户端的真实IP,我们的线上经过负载后是 X-Forwarded-For,而测试服务器没有,所以是X-Real-IP
"RealIpHeader": "X-Real-IP",
"ClientIdHeader": "X-ClientId",
"HttpStatusCode": 200,
"QuotaExceededResponse": {
"Content": "{{\"state\":429,\"message\":\"访问过于频繁,请稍后重试\",\"data\":null}}",
"ContentType": "application/json",
"StatusCode": 200
},
//IP白名单,本地调试或者UAT环境,可以加入相应的IP,略过策略的限制
"IpWhitelist": [],
//端点白名单,如果全局配置了访问策略,设置端点白名单相当于IP白名单一样,略过策略的限制
"EndpointWhitelist": [],
"ClientWhitelist": [],
"GeneralRules": [
{
"Endpoint": "post:/api/second/savevalue",
"Period": "1s",
"Limit": 1
},
{
"Endpoint": "post:/api/second/savedata",
"Period": "1s",
"Limit": 1
},
{
"Endpoint": "*:/api/second/other/save",
"Period": "1s",
"Limit": 1
}
]
}
}
......@@ -34,9 +34,6 @@ public void ConfigureServices(IServiceCollection services)
// memory cache
services.AddMemoryCache();
// ratelimit
// services.AddRateLimitConfiguration(Configuration);
// graphql
services.AddGraphQLSchemaAndTypes();
......@@ -104,12 +101,12 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
app.UseStatusCodePagesWithReExecute("/error/{0}");
}
app.UseMiddleware<RequestRateLimitingMiddleware>();
app.UseCors("SignalrCore");
app.UseSignalR(routes => routes.MapHub<AllotLogHub>("/performance/allotLogHub"));
// app.UseRateLimitSetup();
app.UseMvc();
app.UseSwaggerSetup(Configuration);
......
......@@ -36,5 +36,10 @@
"ImportFile": "http://localhost:5001/api/",
// 抽取uri
"HttpPost": "http://localhost:50997/api/"
},
"RateLimitingConfig": {
"Endpoints": [ "/api/second/savevalue", "/api/second/savedata", "/api/second/other/save" ],
"Period": "1", // 单位为秒
"Limit": 1
}
}
......@@ -74,6 +74,21 @@
相对
</summary>
</member>
<member name="P:Performance.DtoModels.AppSettings.RateLimitingConfig.Endpoints">
<summary>
路径
</summary>
</member>
<member name="P:Performance.DtoModels.AppSettings.RateLimitingConfig.Period">
<summary>
周期,单位为秒
</summary>
</member>
<member name="P:Performance.DtoModels.AppSettings.RateLimitingConfig.Limit">
<summary>
请求次数
</summary>
</member>
<member name="P:Performance.DtoModels.AppSettings.WebapiUrl.ImportFile">
<summary>
上传文件地址
......
namespace Performance.DtoModels.AppSettings
{
public class RateLimitingConfig
{
/// <summary>
/// 路径
/// </summary>
public string[] Endpoints { get; set; }
/// <summary>
/// 周期,单位为秒
/// </summary>
public double Period { get; set; }
/// <summary>
/// 请求次数
/// </summary>
public int Limit { get; set; }
}
}
......@@ -13,5 +13,6 @@ public enum ResponseType
NotFound = 5,
ParameterError = 6,
Disable = 7,
TooManyRequests = 8,
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment