/// <summary>
        /// check single rule for target
        /// </summary>
        /// <param name="target"></param>
        /// <param name="rule"></param>
        /// <returns></returns>
        protected override RuleCheckResult CheckSingleRule(string target, RateLimitRule rule, RateLimitTypeAttributeJson rateLimitAttrData = null)
        {
            var currentRule = rule as SlidingWindowRule;
            var amount      = 1;

            #region local attribute

            if (rateLimitAttrData != null && rateLimitAttrData.SlidingWindowLimitAttribute != null)
            {
                currentRule.LimitNumber  = rateLimitAttrData.SlidingWindowLimitAttribute.LimitNumber;
                currentRule.StatWindow   = CommonUtils.Parse(rateLimitAttrData.SlidingWindowLimitAttribute.StatWindowPeriod);
                currentRule.StatPeriod   = CommonUtils.Parse(rateLimitAttrData.SlidingWindowLimitAttribute.StatSmallPeriod);
                currentRule.PeriodNumber = (int)(currentRule.StatWindow.TotalMilliseconds / currentRule.StatPeriod.TotalMilliseconds);
            }

            #endregion local attribute

            var  currentTime        = _timeProvider.GetCurrentUtcMilliseconds();
            var  startTime          = AlgorithmStartTime.ToSpecifiedTypeTime(currentTime, currentRule.StatWindow, currentRule.StartTimeType);
            long expireMilliseconds = ((long)currentRule.StatWindow.TotalMilliseconds) * 2;
            long periodMilliseconds = (long)currentRule.StatPeriod.TotalMilliseconds;

            var ret = (long[])EvaluateScript(_slidingWindowIncrementLuaScript,
                                             new RedisKey[] { target },
                                             new RedisValue[] { amount, expireMilliseconds, periodMilliseconds, currentRule.PeriodNumber, currentTime, startTime, currentRule.LimitNumber, currentRule.LockSeconds });
            return(new RuleCheckResult()
            {
                IsLimit = ret[0] == 0 ? false : true,
                Target = target,
                Count = ret[1],
                Rule = rule,
                RateLimitExceptionThrow = currentRule.RateLimitExceptionThrow
            });
        }
        private Tuple <bool, long> InnerCheckSingleRule(string target, int amount, FixedWindowRule currentRule)
        {
            bool locked = CheckLocked(target);

            if (locked)
            {
                return(Tuple.Create(true, -1L));
            }

            var currentTime = _timeProvider.GetCurrentLocalTime();
            var startTime   = AlgorithmStartTime.ToSpecifiedTypeTime(currentTime, currentRule.StatWindow, currentRule.StartTimeType);

            Tuple <bool, long> incrementResult;

            lock (target)
            {
                DateTimeOffset expireTime = startTime.Add(currentRule.StatWindow);
                incrementResult = SimpleIncrement(target, amount, expireTime, currentRule.LimitNumber);
            }

            var checkResult = incrementResult.Item1;

            if (checkResult)
            {
                if (currentRule.LockSeconds > 0)
                {
                    TryLock(target, currentTime, TimeSpan.FromSeconds(currentRule.LockSeconds));
                    return(Tuple.Create(checkResult, incrementResult.Item2));
                }
            }

            return(Tuple.Create(checkResult, incrementResult.Item2));
        }
Beispiel #3
0
        /// <summary>
        /// Get expire time from natural period beign
        /// </summary>
        /// <param name="statWindow"></param>
        /// <param name="now"></param>
        /// <returns></returns>
        protected long GetExpireTimeFromNaturalPeriodBeign(TimeSpan statWindow, DateTimeOffset now)
        {
            DateTimeOffset startTime = AlgorithmStartTime.ToNaturalPeriodBeignTime(now, statWindow);
            DateTimeOffset endTime   = startTime.Add(statWindow);

            return((long)endTime.Subtract(now).TotalMilliseconds);
        }
        /// <summary>
        /// check single rule for target
        /// </summary>
        /// <param name="target"></param>
        /// <param name="rule"></param>
        /// <returns></returns>
        protected override RuleCheckResult CheckSingleRule(string target, RateLimitRule rule, RateLimitTypeAttributeJson rateLimitAttrData = null)
        {
            var currentRule = rule as LeakyBucketRule;
            var amount      = 1;

            #region local attribute

            if (rateLimitAttrData != null && rateLimitAttrData.LeakyBucketLimitAttribute != null)
            {
                currentRule.Capacity = rateLimitAttrData.LeakyBucketLimitAttribute.Capacity;
                currentRule.OutflowQuantityPerUnit  = rateLimitAttrData.LeakyBucketLimitAttribute.OutflowQuantityPerUnit;
                currentRule.OutflowUnit             = CommonUtils.Parse(rateLimitAttrData.LeakyBucketLimitAttribute.Period);
                currentRule.RateLimitExceptionThrow = rateLimitAttrData.LeakyBucketLimitAttribute.RateLimitExceptionThrow;
            }

            #endregion local attribute

            var outflowUnit = currentRule.OutflowUnit.TotalMilliseconds;
            var currentTime = _timeProvider.GetCurrentUtcMilliseconds();
            var startTime   = AlgorithmStartTime.ToSpecifiedTypeTime(currentTime, TimeSpan.FromMilliseconds(outflowUnit), currentRule.StartTimeType);

            var ret = (long[])EvaluateScript(_leakyBucketIncrementLuaScript, new RedisKey[] { target },
                                             new RedisValue[] { amount, currentRule.Capacity, outflowUnit, currentRule.OutflowQuantityPerUnit, currentTime, startTime, currentRule.LockSeconds });
            return(new RuleCheckResult()
            {
                IsLimit = ret[0] == 0 ? false : true,
                Target = target,
                Count = ret[1],
                Rule = rule,
                Wait = ret[2],
                RateLimitExceptionThrow = currentRule.RateLimitExceptionThrow
            });
        }
Beispiel #5
0
        private long GetCurrentPeriod(string target, SlidingWindowRule currentRule, long currentMilliseconds)
        {
            long   statPeriodMilliseconds = (long)currentRule.StatPeriod.TotalMilliseconds;
            long   currentPeriod          = 0;
            string ltKey = $"{target}-lt";

            var currentPeriodObj = _cache.Get(ltKey);

            if (currentPeriodObj == null)
            {
                var startTimeMilliseconds = AlgorithmStartTime.ToSpecifiedTypeTime(currentMilliseconds, currentRule.StatWindow, currentRule.StartTimeType);
                currentPeriod = startTimeMilliseconds + statPeriodMilliseconds - 1;
                _cache.Add(ltKey, currentPeriod, DateTimeOffset.MaxValue);
            }
            else
            {
                currentPeriod = (long)currentPeriodObj;
                if (currentMilliseconds > currentPeriod)
                {
                    do
                    {
                        currentPeriod += statPeriodMilliseconds;
                    } while (currentMilliseconds > currentPeriod);

                    //_cache.Set(ltKey, currentPeriod, DateTimeOffset.MaxValue);
                }
            }

            return(currentPeriod);
        }
        /// <summary>
        /// async check single rule for target
        /// </summary>
        /// <param name="target"></param>
        /// <param name="rule"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        protected override async Task <RuleCheckResult> CheckSingleRuleAsync(string target, RateLimitRule rule, RateLimitTypeAttributeJson rateLimitAttrData = null)
        {
            var currentRule = rule as TokenBucketRule;
            var amount      = 1;

            //check controller of method mark TokenbucketAttribute is priority compare with global service.AddRamitLimit
            if (rateLimitAttrData != null && rateLimitAttrData.TokenBucketLimitAttribute != null)
            {
                currentRule.Capacity = rateLimitAttrData.TokenBucketLimitAttribute.Capacity;
                currentRule.InflowQuantityPerUnit   = rateLimitAttrData.TokenBucketLimitAttribute.InflowQuantityPerUnit;
                currentRule.InflowUnit              = CommonUtils.Parse(rateLimitAttrData.TokenBucketLimitAttribute.Period);
                currentRule.RateLimitExceptionThrow = rateLimitAttrData.TokenBucketLimitAttribute.RateLimitExceptionThrow;
            }
            var inflowUnit  = currentRule.InflowUnit.TotalMilliseconds;
            var currentTime = await _timeProvider.GetCurrentUtcMillisecondsAsync();

            var startTime = AlgorithmStartTime.ToSpecifiedTypeTime(currentTime, TimeSpan.FromMilliseconds(inflowUnit), currentRule.StartTimeType);

            var ret = (long[]) await EvaluateScriptAsync(_tokenBucketDecrementLuaScript, new RedisKey[] { target },
                                                         new RedisValue[] { amount, currentRule.Capacity, inflowUnit, currentRule.InflowQuantityPerUnit, currentTime, startTime, currentRule.LockSeconds });

            var result = new Tuple <bool, long>(ret[0] == 0 ? false : true, ret[1]);

            return(new RuleCheckResult()
            {
                IsLimit = result.Item1,
                Target = target,
                Count = result.Item2,
                Rule = rule,
                RateLimitExceptionThrow = currentRule.RateLimitExceptionThrow
            });
        }
        /// <summary>
        /// check single rule for target
        /// </summary>
        /// <param name="target"></param>
        /// <param name="rule"></param>
        /// <returns></returns>
        protected override RuleCheckResult CheckSingleRule(string target, RateLimitRule rule)
        {
            var currentRule = rule as TokenBucketRule;
            var amount      = 1;

            var inflowUnit  = currentRule.InflowUnit.TotalMilliseconds;
            var currentTime = _timeProvider.GetCurrentUtcMilliseconds();
            var startTime   = AlgorithmStartTime.ToSpecifiedTypeTime(currentTime, TimeSpan.FromMilliseconds(inflowUnit), currentRule.StartTimeType);

            var ret = (long[])EvaluateScript(_tokenBucketDecrementLuaScript, new RedisKey[] { target },
                                             new RedisValue[] { amount, currentRule.Capacity, inflowUnit, currentRule.InflowQuantityPerUnit, currentTime, startTime, currentRule.LockSeconds });

            return(new RuleCheckResult()
            {
                IsLimit = ret[0] == 0 ? false : true,
                Target = target,
                Count = ret[1],
                Rule = rule
            });
        }
        public void TestToNaturalPeriodBeignTime()
        {
            DateTimeOffset startTime = DateTimeOffset.Parse("2021-12-21 21:21:21.211");

            var startTime1 = AlgorithmStartTime.ToNaturalPeriodBeignTime(startTime, TimeSpan.Parse("1"));

            Assert.AreEqual("2021-12-21 00:00:00.000", startTime1.ToString("yyyy-MM-dd HH:mm:ss.fff"));

            var startTime2 = AlgorithmStartTime.ToNaturalPeriodBeignTime(startTime, TimeSpan.Parse("0.01:00:00"));

            Assert.AreEqual("2021-12-21 21:00:00.000", startTime2.ToString("yyyy-MM-dd HH:mm:ss.fff"));

            var startTime3 = AlgorithmStartTime.ToNaturalPeriodBeignTime(startTime, TimeSpan.Parse("0.00:01:00"));

            Assert.AreEqual("2021-12-21 21:21:00.000", startTime3.ToString("yyyy-MM-dd HH:mm:ss.fff"));

            var startTime4 = AlgorithmStartTime.ToNaturalPeriodBeignTime(startTime, TimeSpan.Parse("0.00:00:01"));

            Assert.AreEqual("2021-12-21 21:21:21.000", startTime4.ToString("yyyy-MM-dd HH:mm:ss.fff"));
        }
        public void TestToSpecifiedTypeTime()
        {
            DateTimeOffset startTime   = DateTimeOffset.Parse("2021-12-21 21:21:21.211");
            long           startTimeTs = startTime.ToUnixTimeMilliseconds();

            var startTime1 = AlgorithmStartTime.ToSpecifiedTypeTime(startTime, TimeSpan.Parse("1"), StartTimeType.FromCurrent);

            Assert.AreEqual("2021-12-21 21:21:21.211", startTime1.ToString("yyyy-MM-dd HH:mm:ss.fff"));

            var startTime2 = AlgorithmStartTime.ToSpecifiedTypeTime(startTime, TimeSpan.Parse("1"), StartTimeType.FromNaturalPeriodBeign);

            Assert.AreEqual("2021-12-21 00:00:00.000", startTime2.ToString("yyyy-MM-dd HH:mm:ss.fff"));

            var startTime3 = AlgorithmStartTime.ToSpecifiedTypeTime(startTimeTs, TimeSpan.Parse("1"), StartTimeType.FromCurrent);

            Assert.AreEqual(startTimeTs, startTime3);

            var startTime4 = AlgorithmStartTime.ToSpecifiedTypeTime(startTimeTs, TimeSpan.Parse("1"), StartTimeType.FromNaturalPeriodBeign);

            Assert.AreEqual(1640044800000, startTime4);
        }
Beispiel #10
0
        /// <summary>
        /// check single rule for target
        /// </summary>
        /// <param name="target"></param>
        /// <param name="rule"></param>
        /// <returns></returns>
        protected override RuleCheckResult CheckSingleRule(string target, RateLimitRule rule)
        {
            var currentRule = rule as SlidingWindowRule;
            var amount      = 1;

            var  currentTime        = _timeProvider.GetCurrentUtcMilliseconds();
            var  startTime          = AlgorithmStartTime.ToSpecifiedTypeTime(currentTime, currentRule.StatWindow, currentRule.StartTimeType);
            long expireMilliseconds = ((long)currentRule.StatWindow.TotalMilliseconds) * 2;
            long periodMilliseconds = (long)currentRule.StatPeriod.TotalMilliseconds;

            var ret = (long[])EvaluateScript(_slidingWindowIncrementLuaScript,
                                             new RedisKey[] { target },
                                             new RedisValue[] { amount, expireMilliseconds, periodMilliseconds, currentRule.PeriodNumber, currentTime, startTime, currentRule.LimitNumber, currentRule.LockSeconds });

            return(new RuleCheckResult()
            {
                IsLimit = ret[0] == 0 ? false : true,
                Target = target,
                Count = ret[1],
                Rule = rule
            });
        }
        /// <summary>
        /// async check single rule for target
        /// </summary>
        /// <param name="target"></param>
        /// <param name="rule"></param>
        /// <returns></returns>
        protected override async Task <RuleCheckResult> CheckSingleRuleAsync(string target, RateLimitRule rule)
        {
            var currentRule = rule as LeakyBucketRule;
            var amount      = 1;

            // can not call redis TIME command in script
            var outflowUnit = currentRule.OutflowUnit.TotalMilliseconds;
            var currentTime = await _timeProvider.GetCurrentUtcMillisecondsAsync();

            var startTime = AlgorithmStartTime.ToSpecifiedTypeTime(currentTime, TimeSpan.FromMilliseconds(outflowUnit), currentRule.StartTimeType);

            var ret = (long[]) await EvaluateScriptAsync(_leakyBucketIncrementLuaScript, new RedisKey[] { target },
                                                         new RedisValue[] { amount, currentRule.Capacity, outflowUnit, currentRule.OutflowQuantityPerUnit, currentTime, startTime, currentRule.LockSeconds });

            return(new RuleCheckResult()
            {
                IsLimit = ret[0] == 0 ? false : true,
                Target = target,
                Count = ret[1],
                Rule = rule,
                Wait = ret[2],
            });
        }
Beispiel #12
0
        /// <summary>
        /// Increase the count value of the rate limit target for leaky bucket algorithm.
        /// </summary>
        /// <param name="target">The target</param>
        /// <param name="amount">amount of increase</param>
        /// <param name="currentRule">The current rule</param>
        /// <returns>Amount of request in the bucket</returns>
        public Tuple <bool, long, long> InnerCheckSingleRule(string target, long amount, LeakyBucketRule currentRule)
        {
            bool locked = CheckLocked(target);

            if (locked)
            {
                return(Tuple.Create(true, -1L, -1L));
            }

            var outflowUnit = (int)currentRule.OutflowUnit.TotalMilliseconds;
            var currentTime = _timeProvider.GetCurrentLocalTime();

            lock (target)
            {
                var countData = _cache.GetCacheItem(target);
                if (countData == null)
                {
                    var startTime = AlgorithmStartTime.ToSpecifiedTypeTime(currentTime, TimeSpan.FromMilliseconds(outflowUnit), currentRule.StartTimeType);
                    _cache.Add(target, new CountValue(amount)
                    {
                        LastFlowTime = startTime
                    }, DateTimeOffset.MaxValue);
                    return(Tuple.Create(false, 0L, 0L));
                }

                var countValue           = (CountValue)countData.Value;
                var lastTime             = countValue.LastFlowTime;
                var pastTime             = currentTime - lastTime;
                var lastTimeChanged      = false;
                var pastTimeMilliseconds = pastTime.TotalMilliseconds;

                long newCount = 0;
                long wait     = 0;
                if (pastTimeMilliseconds < outflowUnit)
                {
                    newCount = countValue.Value + amount;
                    if (newCount <= currentRule.Capacity + currentRule.OutflowQuantityPerUnit)
                    {
                        var currentUnitRestTime = outflowUnit - pastTimeMilliseconds;
                        wait = CalculateWaitTime(currentRule.OutflowQuantityPerUnit, outflowUnit, newCount, currentUnitRestTime);
                    }
                    else
                    {
                        if (currentRule.LockSeconds > 0)
                        {
                            TryLock(target, currentTime, TimeSpan.FromSeconds(currentRule.LockSeconds));
                        }
                        return(Tuple.Create(true, currentRule.Capacity, -1L));
                    }
                }
                else
                {
                    var pastOutflowUnitQuantity = (int)(pastTimeMilliseconds / outflowUnit);
                    lastTime        = lastTime.AddMilliseconds(pastOutflowUnitQuantity * outflowUnit);
                    lastTimeChanged = true;

                    if (countValue.Value < currentRule.OutflowQuantityPerUnit)
                    {
                        newCount = amount;
                        wait     = 0;
                    }
                    else
                    {
                        var pastOutflowQuantity = currentRule.OutflowQuantityPerUnit * pastOutflowUnitQuantity;
                        newCount = countValue.Value - pastOutflowQuantity + amount;
                        newCount = newCount > 0 ? newCount : amount;
                        var currentUnitRestTime = outflowUnit - (currentTime - lastTime).TotalMilliseconds;
                        wait = CalculateWaitTime(currentRule.OutflowQuantityPerUnit, outflowUnit, newCount, currentUnitRestTime);
                    }
                }

                countValue.Value = newCount;
                if (lastTimeChanged)
                {
                    countValue.LastFlowTime = lastTime;
                }

                var viewCount = newCount - currentRule.OutflowQuantityPerUnit;
                viewCount = viewCount < 0 ? 0 : viewCount;
                return(Tuple.Create(false, viewCount, wait));
            }
        }
        /// <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));
            }
        }