private IAlgorithm GetTokenBucketProcessor(string storageType, int limitNumber)
        {
            var tokenBucketRules = new TokenBucketRule[]
            {
                new TokenBucketRule(limitNumber, 100, TimeSpan.FromSeconds(1))
                {
                    Id            = Guid.NewGuid().ToString(),
                    LockSeconds   = 1,
                    ExtractTarget = (request) =>
                    {
                        return((request as SimulationRequest).RequestResource);
                    },
                    CheckRuleMatching = (request) =>
                    {
                        return(true);
                    },
                }
            };

            if (storageType == "redis")
            {
                return(new RedisTokenBucketAlgorithm(tokenBucketRules, redisClient));
            }
            else
            {
                return(new InProcessTokenBucketAlgorithm(tokenBucketRules));
            }
        }
        private IAlgorithm GetAlgorithm(string storageType, int capacity, int inflowQuantity, TimeSpan inflowUnit, int lockSeconds, StartTimeType startTimeType = StartTimeType.FromCurrent)
        {
            var tokenBucketRules = new TokenBucketRule[]
            {
                new TokenBucketRule(capacity, inflowQuantity, inflowUnit)
                {
                    Id            = Guid.NewGuid().ToString(),
                    LockSeconds   = lockSeconds,
                    StartTimeType = startTimeType,
                    ExtractTarget = (request) =>
                    {
                        return((request as SimulationRequest).RequestResource);
                    },
                    CheckRuleMatching = (request) =>
                    {
                        return(true);
                    },
                    ExtractTargetAsync = (request) =>
                    {
                        return(Task.FromResult((request as SimulationRequest).RequestResource));
                    },
                    CheckRuleMatchingAsync = (request) =>
                    {
                        return(Task.FromResult(true));
                    },
                }
            };

            if (storageType == "redis")
            {
                var redisClient = StackExchange.Redis.ConnectionMultiplexer.Connect("127.0.0.1");
                return(new RedisTokenBucketAlgorithm(tokenBucketRules, redisClient));
            }
            else
            {
                return(new InProcessTokenBucketAlgorithm(tokenBucketRules));
            }
        }
        /// <summary>
        /// Decrease the count value of the rate limit target for token bucket algorithm.
        /// </summary>
        /// <param name="target">The target</param>
        /// <param name="amount">The amount of decrease</param>
        /// <param name="currentRule">The rate limit rule</param>
        /// <returns>Amount of token in the bucket</returns>
        public Tuple <bool, long> InnerCheckSingleRule(string target, long amount, TokenBucketRule currentRule)
        {
            bool locked = CheckLocked(target);

            if (locked)
            {
                return(new Tuple <bool, long>(true, -1));
            }

            var inflowUnit = currentRule.InflowUnit.TotalMilliseconds;

            lock (target)
            {
                var  currentTime  = _timeProvider.GetCurrentLocalTime();
                long bucketAmount = 0;
                var  result       = _cache.GetCacheItem(target);
                if (result == null)
                {
                    var startTime = AlgorithmStartTime.ToSpecifiedTypeTime(currentTime, TimeSpan.FromMilliseconds(inflowUnit), currentRule.StartTimeType);
                    bucketAmount = currentRule.Capacity - amount;
                    _cache.Add(target, new CountValue(bucketAmount)
                    {
                        LastFlowTime = startTime
                    }, DateTimeOffset.MaxValue);
                    return(new Tuple <bool, long>(false, bucketAmount));
                }

                var countValue           = (CountValue)result.Value;
                var lastTime             = countValue.LastFlowTime;
                var lastTimeChanged      = false;
                var pastTime             = currentTime - lastTime;
                var pastTimeMilliseconds = pastTime.TotalMilliseconds;
                // Debug.WriteLine(currentTime.ToString("mm:ss.fff") + "," + lastTime.ToString("mm:ss.fff") + "," + pastTimeMilliseconds);
                if (pastTimeMilliseconds < inflowUnit)
                {
                    bucketAmount = countValue.Value - amount;
                }
                else
                {
                    var pastInflowUnitQuantity = (int)(pastTimeMilliseconds / inflowUnit);
                    lastTime        = lastTime.AddMilliseconds(pastInflowUnitQuantity * inflowUnit);
                    lastTimeChanged = true;
                    var pastInflowQuantity = currentRule.InflowQuantityPerUnit * pastInflowUnitQuantity;
                    bucketAmount = (countValue.Value < 0 ? 0 : countValue.Value) + pastInflowQuantity - amount;
                }

                if (bucketAmount < 0)
                {
                    if (currentRule.LockSeconds > 0)
                    {
                        TryLock(target, currentTime, TimeSpan.FromSeconds(currentRule.LockSeconds));
                    }

                    return(new Tuple <bool, long>(true, bucketAmount));
                }

                if (bucketAmount >= currentRule.Capacity)
                {
                    bucketAmount = currentRule.Capacity - amount;
                }

                countValue.Value = bucketAmount;
                if (lastTimeChanged)
                {
                    countValue.LastFlowTime = lastTime;
                }
                return(new Tuple <bool, long>(false, countValue.Value));
            }
        }