public static void ConfigureRateLimiting(HttpFilterCollection filters) { var rateLimitingPolicyParametersProvider = new SampleRateLimitingClientPolicyProvider(); var globalRateLimitingClientPolicyManager = new RateLimitingPolicyManager(rateLimitingPolicyParametersProvider) .AddPathToWhiteList("/api/unlimited") .AddPoliciesForAllEndpoints(new List <AllowedConsumptionRate>() { new AllowedConsumptionRate(100, RateLimitUnit.PerMinute) }, allowAttributeOverride: true, name: "StaticPolicy_2") .AddEndpointPolicy("/api/globallylimited/{id}", "GET", new List <AllowedConsumptionRate>() { new AllowedConsumptionRate(5, RateLimitUnit.PerMinute), new AllowedConsumptionRate(8, RateLimitUnit.PerHour) }, true, "StaticPolicy_0") .AddEndpointPolicy("/api/globallylimited/{id}/sub/{subid}", RateLimitPolicy.AllHttpMethods, new List <AllowedConsumptionRate>() { new AllowedConsumptionRate(2, RateLimitUnit.PerMinute) }, true, "StaticPolicy_1"); #region Setting up the Redis rate limiter var redisRateLimiterSettings = new RedisRateLimiterSettings(); ConfigureRateLimitingSettings(redisRateLimiterSettings); var rateLimitCacheProvider = new SlidingTimeWindowRateLimiter( redisRateLimiterSettings.RateLimitRedisCacheConnectionString, onThrottled: (rateLimitingResult) => { //_logger.LogInformation( // "Request throttled for client {ClientId} and endpoint {Endpoint}", // rateLimitingResult.CacheKey.RequestId, // rateLimitingResult.CacheKey.RouteTemplate); }, circuitBreaker: new DefaultCircuitBreaker(redisRateLimiterSettings.FaultThreshholdPerWindowDuration, redisRateLimiterSettings.FaultWindowDurationInMilliseconds, redisRateLimiterSettings.CircuitOpenIntervalInSecs, onCircuitOpened: () => { //_logger.LogWarning("Rate limiting circuit opened") }, onCircuitClosed: () => { //logger.LogWarning("Rate limiting circuit closed") })); #endregion filters.Add(new RateLimitingFilter(new RateLimiter(rateLimitCacheProvider, globalRateLimitingClientPolicyManager), filters)); }
public static void Main(string[] args) { IRateLimitingCacheProvider rateLimiter = new SlidingTimeWindowRateLimiter("localhost"); var policies = new List <RateLimitPolicy>() { new RateLimitPolicy("test_client_throttle_1", new List <AllowedConsumptionRate>() { new AllowedConsumptionRate(2, RateLimitUnit.PerSecond) }), new RateLimitPolicy("test_client_throttle_2", new List <AllowedConsumptionRate>() { new AllowedConsumptionRate(2, RateLimitUnit.PerSecond), new AllowedConsumptionRate(10, RateLimitUnit.PerMinute) }), new RateLimitPolicy("test_client_throttle_3", new List <AllowedConsumptionRate>() { new AllowedConsumptionRate(3, RateLimitUnit.PerSecond), new AllowedConsumptionRate(10, RateLimitUnit.PerMinute) }), new RateLimitPolicy("test_client_throttle_4", new List <AllowedConsumptionRate>() { new AllowedConsumptionRate(3, RateLimitUnit.PerSecond), new AllowedConsumptionRate(10, RateLimitUnit.PerMinute) }) }; const int numberOfThreads = 10; Console.WriteLine("Using RedisSlidingWindowRateLimiter"); Console.WriteLine(""); Console.WriteLine($"Running limiter with a policy of 2 allowed call rates (2 perSecond and 10 perMinute) with 10 Threads each making 100 requests"); var taskArray = new Task[numberOfThreads]; Parallel.ForEach <int>(Enumerable.Range(0, numberOfThreads).ToArray(), (_) => { PrintAverageTimeInMilliseconds(new { TotalNumberOfCalls = 100, RateLimiter = rateLimiter, Policy = new RateLimitPolicy("test_client_perf", new List <AllowedConsumptionRate>() { new AllowedConsumptionRate(2, RateLimitUnit.PerMinute) }) }); }); Console.WriteLine(""); Console.WriteLine("Starting throttle count verifications"); Console.WriteLine(""); Console.WriteLine("Starting throttle count verification for 100 requests with a policy (2 perSecond) with a delay of 100 milliseconds"); Console.WriteLine($"Excepted ThrottleCount around 80 and Actual Throttle Count = " + $"{GetThrottleCount(rateLimiter, policies[0], 100, 100)}"); Console.WriteLine(""); Console.WriteLine("Starting throttle count verification for 100 requests with a policy of 2 allowed call rates (2 perSecond and 10 perMinute) with a delay of 2 milliseconds"); Console.WriteLine($"Excepted ThrottleCount around 90 and Actual ThrottleCount = {GetThrottleCount(rateLimiter, policies[1], 100, 100)}"); Console.WriteLine(""); Console.WriteLine("Starting throttle count verification for 100 requests with a policy of 2 allowed call rates (3 perSecond and 10 perMinute) with a delay of 2 milliseconds"); Console.WriteLine($"Excepted ThrottleCount around 97 and Actual ThrottleCount = " + $"Count {GetThrottleCount(rateLimiter, policies[2], 100, 2)}"); Console.WriteLine(""); Console.WriteLine("Starting throttle count verification for 100 requests with a policy of 2 allowed call rates (3 perSecond and 10 perMinute) with a delay of 100 milliseconds"); Console.WriteLine($"Excepted ThrottleCount around 90 and Actual ThrottleCount = " + $"Count {GetThrottleCount(rateLimiter, policies[3], 100, 100)}"); }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { // Adds services required for using options. services.AddOptions(); #region Setting policies explicitly using code var rateLimitingPolicyParametersProvider = new SampleRateLimitingClientPolicyProvider(); var globalRateLimitingClientPolicyManager = new RateLimitingPolicyManager(rateLimitingPolicyParametersProvider) .AddPathToWhiteList("/api/unlimited") .AddPoliciesForAllEndpoints(new List <AllowedConsumptionRate>() { new AllowedConsumptionRate(1000, RateLimitUnit.PerMinute) }, allowAttributeOverride: true, name: "StaticPolicy_2") .AddEndpointPolicy("/api/globallylimited/{id}", "GET", new List <AllowedConsumptionRate>() { new AllowedConsumptionRate(5, RateLimitUnit.PerMinute), new AllowedConsumptionRate(8, RateLimitUnit.PerHour) }, true, "StaticPolicy_0") .AddEndpointPolicy("/api/globallylimited/{id}/sub/{subid}", RateLimitPolicy.AllHttpMethods, new List <AllowedConsumptionRate>() { new AllowedConsumptionRate(2, RateLimitUnit.PerMinute) }, true, "StaticPolicy_1"); #endregion #region Setting Policies Using Configuration Options // var rateLimitingOptions = new RateLimitingOptions(); // Configuration.GetSection(nameof(RateLimitingOptions)).Bind(rateLimitingOptions); // var globalRateLimitingClientPolicyManager = new RateLimitingPolicyManager( // new SampleRateLimitingClientPolicyProvider()) // .AddPoliciesForAllEndpoints(new List<AllowedCallRate>() {new AllowedCallRate(180, RateLimitUnit.PerMinute)},name:"ClientPolicy") // .AddPathsToWhiteList(rateLimitingOptions.RateLimitingWhiteListedPaths) // .AddRequestKeysToWhiteList(rateLimitingOptions.RateLimitingWhiteListedRequestKeys); #endregion #region Setting up the Redis rate limiter var redisRateLimiterSettings = new RedisRateLimiterSettings(); Configuration.GetSection(nameof(RedisRateLimiterSettings)).Bind(redisRateLimiterSettings); var rateLimitCacheProvider = new SlidingTimeWindowRateLimiter( redisRateLimiterSettings.RateLimitRedisCacheConnectionString, (exp) => _logger.LogError("Error in rate limiting", exp), onThrottled: (rateLimitingResult) => { _logger.LogInformation( "Request throttled for client {ClientId} and endpoint {Endpoint}", rateLimitingResult.CacheKey.RequestId, rateLimitingResult.CacheKey.RouteTemplate); }, circuitBreaker: new DefaultCircuitBreaker(redisRateLimiterSettings.FaultThreshholdPerWindowDuration, redisRateLimiterSettings.FaultWindowDurationInMilliseconds, redisRateLimiterSettings.CircuitOpenIntervalInSecs, onCircuitOpened: () => _logger.LogWarning("Rate limiting circuit opened"), onCircuitClosed: () => _logger.LogWarning("Rate limiting circuit closed"))); #endregion // Add framework services services.AddMvc(options => { #region Adding the RateLimitingFilter options.Filters.Add(new RateLimitingFilter( new RateLimiter(rateLimitCacheProvider, globalRateLimitingClientPolicyManager))); #endregion #region Multi level rate limiting - Multiple action filters based on separate Policy Providers providing separate policies //options.Filters.Add(new RateLimitingFilter( // new RateLimiter(rateLimitCacheProvider, new SampleRateLimitingUserPolicyProvider()))); //options.Filters.Add(new RateLimitingFilter( // new RateLimiter(rateLimitCacheProvider, new SampleRateLimitingOrganizationPolicyProvider()))); #endregion }); }
Arrange(string requestId, string method, string routeTemplate, AllowedConsumptionRate allowedCallRate, DateTime utcDateTime, long numberOfRequestsMadeSoFar) { var clockMock = GetClockMock(utcDateTime); var cacheKey = new RateLimitCacheKey(requestId, method, "localhost", routeTemplate, allowedCallRate, _ => allowedCallRate.Unit.ToString(), clockMock.Object); var connectionMultiplexerMock = new Mock <IConnectionMultiplexer>(MockBehavior.Strict); var dbMock = new Mock <IDatabase>(MockBehavior.Strict); var transactionMock = new Mock <ITransaction>(MockBehavior.Strict); transactionMock.Setup(redisTransaction => redisTransaction.SortedSetRemoveRangeByScoreAsync( cacheKey.ToString(), 0, utcDateTime.Ticks - (long)cacheKey.Unit, It.IsAny <Exclude>(), It.IsAny <CommandFlags>())).Returns(Task.FromResult(10L)); transactionMock.Setup(redisTransaction => redisTransaction.SortedSetAddAsync( cacheKey.ToString(), It.IsAny <RedisValue>(), utcDateTime.Ticks, It.IsAny <When>(), It.IsAny <CommandFlags>())).Returns(Task.FromResult(true)); transactionMock.Setup(redisTransaction => redisTransaction.SortedSetLengthAsync( cacheKey.ToString(), It.IsAny <double>(), It.IsAny <double>(), It.IsAny <Exclude>(), It.IsAny <CommandFlags>())).Returns( Task.FromResult(numberOfRequestsMadeSoFar)); transactionMock.Setup(redisTransaction => redisTransaction.KeyExpireAsync( cacheKey.ToString(), cacheKey.Expiration.Add(new TimeSpan(0, 1, 0)), It.IsAny <CommandFlags>())).Returns(Task.FromResult(true)); transactionMock.Setup(redisTransaction => redisTransaction.ExecuteAsync(CommandFlags.None)).Returns(Task.FromResult(true)); var postViolationTransactionMock = new Mock <ITransaction>(MockBehavior.Strict); postViolationTransactionMock.Setup(redisTransaction => redisTransaction.SortedSetRangeByRankWithScoresAsync( cacheKey.ToString(), 0, 0, It.IsAny <Order>(), It.IsAny <CommandFlags>())) .Returns(Task.FromResult(new SortedSetEntry[] { new SortedSetEntry(It.IsAny <RedisValue>(), utcDateTime.Ticks) })); postViolationTransactionMock.Setup(redisTransaction => redisTransaction.ExecuteAsync(CommandFlags.None)).Returns(Task.FromResult(true)); dbMock.SetupSequence(db => db.CreateTransaction(null)) .Returns(transactionMock.Object) .Returns(postViolationTransactionMock.Object); connectionMultiplexerMock.Setup(connection => connection.IsConnected).Returns(true); connectionMultiplexerMock.Setup(connection => connection.GetDatabase(-1, null)).Returns(dbMock.Object); var rateLimiter = new SlidingTimeWindowRateLimiter("http://localhost", clock: clockMock.Object, connectToRedisFunc: async() => await Task.FromResult(connectionMultiplexerMock.Object), countThrottledRequests: true); return(rateLimiter, connectionMultiplexerMock, dbMock, transactionMock, postViolationTransactionMock, clockMock); }