stefanprodan / AspNetCoreRateLimit

ASP.NET Core rate limiting middleware

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Redis Cache Rate Limiting to InMemory FallBack Not Working

manisha201301 opened this issue · comments

Hello everyone.,

I have been trying to implement Redis Cache rate limiting which falls back to In-Memory rate limiting. I am trying to implement IRateLimitCounterStore (using the entire class ) just as the Solution2 mentioned here: https://github.com/stefanprodan/AspNetCoreRateLimit/wiki/Using-Redis-as-a-distributed-counter-store

Somehow, it is not even hitting the GetAsync or SetAsync methods, even though the class is injected and passed down the service configuration.

Any ideas on how to implement it? Or what could be going wrong?

I am implementing it like this
Startup.cs

public void Configure(...) 
{
     app.UseCustomRateLimiting();
}

public void CofigureServices() 
{
    services.Configure<ClientRateLimitOptions>(Configuration.GetSection("ClientRateLimiting"));
    services.AddSingleton<IConnectionMultiplexer>(provider => 
    ConnectionMultiplexer.Connect(Configuration[redisConnectionKey]));
    services.AddRedisRateLimiting();

    services.AddSingleton<IRateLimitCounterStore, RedisRateLimitCounterStore>();
    services.AddSingleton<IRateLimitConfiguration, CustomRateLimitConfiguration>();
}

public static class RateLimitingMiddlewareExtensions
{
    public static IApplicationBuilder UseCustomRateLimiting(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<CutomRateLimitingLogging>();
      }
}

This is my Custom class to override LogBlockedRequest

    public class CutomRateLimitingLogging : ClientRateLimitMiddleware
    {
        private readonly IMultiLogger Logger;

        public CutomRateLimitingLogging(RequestDelegate next, IProcessingStrategy processingStrategy, IOptions<ClientRateLimitOptions> options, IRateLimitCounterStore counterStore, IClientPolicyStore policyStore, IRateLimitConfiguration config, ILogger<ClientRateLimitMiddleware> logger, IMultiLogger loggerOut)
            : base(next, processingStrategy, options, counterStore, policyStore, config, logger)
        {
            Logger = loggerOut;
        }

        protected override void LogBlockedRequest(HttpContext httpContext, ClientRequestIdentity identity, RateLimitCounter counter, RateLimitRule rule)
        {
            Logger.LogWarning(...);
        }
    }

I'm hitting the same issue, did you get anywhere with it?

This works fine for me:

// 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<ClientRateLimitOptions>(configuration.GetSection("ClientRateLimiting"));

//load client rules from appsettings.json
services.Configure<ClientRateLimitPolicies>(configuration.GetSection("ClientRateLimitPolicies"));

// inject counter and rules stores
var redisOptions = ConfigurationOptions.Parse(configuration["ConnectionStrings:Redis"]);
//services.AddSingleton<IConnectionMultiplexer>(provider => ConnectionMultiplexer.Connect(redisOptions));

//services.AddRedisRateLimiting();

services.AddSingleton(Options.Create(new RedisOptions(redisOptions.ToString())));
services.AddSingleton<IClientPolicyStore, MemoryCacheClientPolicyStore>();
services.AddSingleton<IRateLimitCounterStore, RedisRateLimitCounterStore>();
services.AddSingleton<IProcessingStrategy, AsyncKeyLockProcessingStrategy>();

// configuration (resolvers, counter key builders)
services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();

RedisOptions.cs

public class RedisOptions
{
    public string ConnectionString { get; }

    public bool Enabled { get; }

    public RedisOptions(string connectionString, bool enabled = true)
    {
        ConnectionString = connectionString;
        Enabled = enabled;
    }
}

I've noticed a significant performance gain after removing the Redis IsConnected check from TryRedisCommandAsync (when Redis is not available):

private async Task<T> TryRedisCommandAsync<T>(Func<Task<T>> command, Func<Task<T>> fallbackCommand)
        {
            if (_redisOptions?.Enabled == true) // && _redis?.IsConnected == true)

The only downside is that there is a concurrency issue with the default AsyncKeyLockProcessingStrategy implementation. The ideal solution would be a custom processing strategy that tries to use RedisProcessingStrategy and falls back to AsyncKeyLockProcessingStrategy.

It would be nice if there was an officially supported option for Redis with In-Memory fallback (or at least a separate package) and it could be simply added via AddRedisRateLimitingWithInMemoryFallback().