.Net Core限流的实现示例(2)

ClientRateLimitPolicies:客户端限流的特殊配置,规则和通用规则一样设置,只不过需要配合ClientIdHeader在请求头中来使用,需要使用app.UseClientRateLimiting();启用,否则无效。这个参数名称是可以更改的噢。通用规则和特殊规则是同优先级的。

IpRateLimitPolicies:IP限流的特殊配置,规则和通用规则一样设置,只不过需要配合RealIpHeader在请求头中来使用,需要使用app.UseIpRateLimiting();启用,否则无效。这个参数名称是可以更改的噢。通用规则和特殊规则是同优先级的。

3.特殊规则的启用

IP和客户端特殊规则的启用需要改造Program文件中的程序入口如下,分别发送各自的特殊规则:

public static async Task Main(string[] args) {   IWebHost webHost = CreateWebHostBuilder(args).Build();   using (var scope = webHost.Services.CreateScope())   {     var clientPolicyStore = scope.ServiceProvider.GetRequiredService<IClientPolicyStore>();     await clientPolicyStore.SeedAsync();     var ipPolicyStore = scope.ServiceProvider.GetRequiredService<IIpPolicyStore>();     await ipPolicyStore.SeedAsync();   }   await webHost.RunAsync(); }

在ConfigureServices中读取配置参数,之后是在Startup文件中的Configure方法选择app.UseIpRateLimiting()或app.UseClientRateLimiting()启动IP特殊规则或者客户端特殊规则,都存在的情况下,先执行的先生效。

 三、请求返回头

限流启动后,执行限流规则的返回头会有三个参数分别为:

X-Rate-Limit-Limit:现在时间,如1d。

X-Rate-Limit-Remaining:剩余可请求次数。

X-Rate-Limit-Reset:下次请求次数重置时间。

多个限制规则会采用最长的周期的规则显示。

在配置文件中配置返回信息,除了返回提示信息外,还可以返回限制规则提醒,如下

"Content": "{{\"code\":429,\"msg\":\"访问太频繁了,每{1}{0}次,请在{2}秒后重试\",\"data\":null}}",

{0}可以替换当前阻止规则规定的次数,{1}可以替换时间区间带单位s、h等,{2}替换几秒后尝试当单位为天或者小时等都会换算成秒。

四、使用Redis存储

限流规则等目前都是通过内存存储的,我们结合实际会使用redis存储。使用Microsoft.Extensions.Caching.Redis可以达到这么目的。

但是好像会存在性能问题,所以我们自己替换,使用的是用CSRedis封装的方法,不过这里不做阐述。

我们缓存三类数据1、访问计数2、ip特殊规则3、客户端特殊规则

1、访问计数

public class RedisRateLimitCounterStore : IRateLimitCounterStore { private readonly ILogger _logger; private readonly IRateLimitCounterStore _memoryCacheStore; private readonly RedisCache _redisCache; public RedisRateLimitCounterStore( IMemoryCache memoryCache, ILogger<RedisRateLimitCounterStore> logger) { _logger = logger; _memoryCacheStore = new MemoryCacheRateLimitCounterStore(memoryCache); _redisCache = new RedisCache(); } public async Task<bool> ExistsAsync(string id, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); return await TryRedisCommandAsync( () => { return _redisCache.KeyExistsAsync(id, 0); }, () => { return _memoryCacheStore.ExistsAsync(id, cancellationToken); }); } public async Task<RateLimitCounter?> GetAsync(string id, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); return await TryRedisCommandAsync( async () => { var value = await _redisCache.GetStringAsync(id, 0); if (!string.IsNullOrEmpty(value)) { return JsonConvert.DeserializeObject<RateLimitCounter?>(value); } return null; }, () => { return _memoryCacheStore.GetAsync(id, cancellationToken); }); } public async Task RemoveAsync(string id, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); _ = await TryRedisCommandAsync( async () => { await _redisCache.KeyDeleteAsync(id, 0); return true; }, async () => { await _memoryCacheStore.RemoveAsync(id, cancellationToken); return true; }); } public async Task SetAsync(string id, RateLimitCounter? entry, TimeSpan? expirationTime = null, CancellationToken cancellationToken = default) { cancellationToken.ThrowIfCancellationRequested(); _ = await TryRedisCommandAsync( async () => { var exprie = expirationTime.HasValue ? Convert.ToInt32(expirationTime.Value.TotalSeconds) : -1; await _redisCache.SetStringAsync(id, JsonConvert.SerializeObject(entry.Value), exprie); return true; }, async () => { await _memoryCacheStore.SetAsync(id, entry, expirationTime, cancellationToken); return true; }); } private async Task<T> TryRedisCommandAsync<T>(Func<Task<T>> command, Func<Task<T>> fallbackCommand) { if (_redisCache != null) { try { return await command(); } catch (Exception ex) { _logger.LogError($"Redis command failed: {ex}"); } } return await fallbackCommand(); } }

 2、ip特殊规则

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyfdgj.html