示例#1
0
        private Tuple <long, long, long> Create(RateQuota rateQuota)
        {
            long limit;
            long csii; //信元发送的间隔时间
            long cdvt; //信元发送时间间隔的变化容限

            switch (rateQuota.PeriodType)
            {
            case RateLimitPeriod.Min:
                csii = TimeSpan.FromTicks(TimeSpan.TicksPerMinute / rateQuota.MaxRate).Ticks;
                break;

            case RateLimitPeriod.Hour:
                csii = TimeSpan.FromTicks(TimeSpan.TicksPerHour / rateQuota.MaxRate).Ticks;
                break;

            case RateLimitPeriod.Day:
                csii = TimeSpan.FromTicks(TimeSpan.TicksPerDay / rateQuota.MaxRate).Ticks;
                break;

            case RateLimitPeriod.Week:
                csii = TimeSpan.FromTicks(TimeSpan.TicksPerDay * 7 / rateQuota.MaxRate).Ticks;
                break;

            default:
                csii = TimeSpan.FromTicks(TimeSpan.TicksPerSecond / rateQuota.MaxRate).Ticks;
                break;
            }

            limit = rateQuota.MaxBurst + 1;
            cdvt  = csii * limit;
            return(Tuple.Create(limit, csii, cdvt));
        }
示例#2
0
        /// <summary>
        /// 参数构造函数
        /// </summary>
        /// <param name="rateQuota"><see cref="RateQuota"/></param>
        /// <param name="identity">唯一标识</param>
        /// <param name="quantity">总量</param>
        /// <returns></returns>
        public RateLimitResult RateLimit(RateQuota rateQuota, string identity, int quantity = 1)
        {
            if (rateQuota.MaxRate < 1)
            {
                throw new IndexOutOfRangeException("MaxBurst必须大于0");
            }
            if (rateQuota.MaxBurst < 1)
            {
                throw new IndexOutOfRangeException("MaxBurst必须大于0");
            }

            var  gcra = Create(rateQuota);
            long limit = gcra.Item1, csii = gcra.Item2, cdvt = gcra.Item3;

            var result = new RateLimitResult {
                MaxLimit = limit, RetryAfter = NoneRetry
            };

            TimeSpan       ttl;
            DateTimeOffset tat, newTat;

            var i = 0;

            while (true)
            {
                var now      = DateTimeOffset.UtcNow;
                var existKey = _rateLimitStore.TryGet(identity, out long tatVal);
                if (existKey)
                {
                    tat = DateTimeOffset.FromUnixTimeMilliseconds(tatVal);
                }
                else
                {
                    tat = now;
                }

                //根据获取数量计算间隔幅度
                var increment = quantity * csii;
                if (now > tat)
                {
                    newTat = now.AddTicks(increment);
                }
                else
                {
                    newTat = tat.AddTicks(increment);
                }

                var allowAt = newTat.AddTicks(-cdvt);
                var diff    = now.Ticks - allowAt.Ticks;
                if (diff < 0)
                {
                    if (increment <= cdvt)
                    {
                        result.RetryAfter = TimeSpan.FromTicks(-diff);
                    }
                    ttl            = tat - now;
                    result.Limited = true;
                    break;
                }

                ttl = newTat - now;

                bool updated;
                if (existKey)
                {
                    updated = _rateLimitStore.Update(identity, newTat.ToUnixTimeMilliseconds(), ttl);
                }
                else
                {
                    updated = _rateLimitStore.Add(identity, newTat.ToUnixTimeMilliseconds(), ttl);
                }
                if (updated)
                {
                    break;
                }

                i++;
                if (i > MaxCASAttempts)
                {
                    throw new CASException($"{identity}速率限制存储更新失败超过{i}次。");
                }
            }

            var next = cdvt - ttl.Ticks;

            if (next > -csii)
            {
                result.Remaining = (int)(next / csii);
            }
            result.ResetAfter = ttl;

            return(result);
        }