コード例 #1
0
        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));
        }
コード例 #2
0
        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)}");
        }
コード例 #3
0
        // 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);
        }