예제 #1
0
        /// <summary>
        /// evaluate lua script
        /// </summary>
        /// <param name="luaScript"></param>
        /// <param name="keys"></param>
        /// <param name="values"></param>
        /// <returns></returns>
        protected RedisResult EvaluateScript(RedisLuaScript luaScript, RedisKey[] keys, RedisValue[] values)
        {
            byte[]    sha1     = luaScript.Load();
            IDatabase dataBase = _redisClient.GetDatabase();

            return(dataBase.ScriptEvaluate(sha1, keys, values));
        }
예제 #2
0
        /// <summary>
        /// async evaluate lua script
        /// </summary>
        /// <param name="luaScript"></param>
        /// <param name="keys"></param>
        /// <param name="values"></param>
        /// <returns></returns>
        protected async Task <RedisResult> EvaluateScriptAsync(RedisLuaScript luaScript, RedisKey[] keys, RedisValue[] values)
        {
            byte[] sha1 = await luaScript.LoadAsync();

            IDatabase dataBase = _redisClient.GetDatabase();

            return(await dataBase.ScriptEvaluateAsync(sha1, keys, values));
        }
예제 #3
0
 /// <summary>
 /// create a new instance
 /// </summary>
 /// <param name="rules">The rate limit rules</param>
 /// <param name="redisClient">The redis client</param>
 /// <param name="timeProvider">The time provider</param>
 /// <param name="updatable">If rules can be updated</param>
 public RedisFixedWindowAlgorithm(IEnumerable <FixedWindowRule> rules, ConnectionMultiplexer redisClient = null, ITimeProvider timeProvider = null, bool updatable = false)
     : base(rules, redisClient, timeProvider, updatable)
 {
     _fixedWindowIncrementLuaScript = new RedisLuaScript(_redisClient, "Src-IncrWithExpireSec",
                                                         @"local ret={}
         local lock_key=KEYS[1] .. '-lock'
         local lock_val=redis.call('get',lock_key)
         if lock_val == '1' then
             ret[1]=1
             ret[2]=-1
             return ret;
         end
         ret[1]=0
         local amount=tonumber(ARGV[1])
         local limit_number=tonumber(ARGV[3])
         local lock_seconds=tonumber(ARGV[4])
         local check_result=false
         local current=redis.call('get',KEYS[1])
         if current~=false then
             current = tonumber(current)
             if(limit_number>=0 and current>=limit_number) then
                 check_result=true
             else
                 redis.call('incrby',KEYS[1],amount)
                 current=current+amount
             end
         else
             redis.call('set',KEYS[1],amount,'PX',ARGV[2])
             current=amount
         end
         ret[2]=current
         if check_result then
             ret[1]=1
             if lock_seconds>0 then
                 redis.call('set',lock_key,'1','EX',lock_seconds,'NX')
             end
         end
         return ret");
 }
예제 #4
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="rules"></param>
        /// <param name="redisClient"></param>
        /// <param name="timeProvider"></param>
        /// <param name="updatable"></param>
        public RedisSlidingWindowAlgorithm(IEnumerable <SlidingWindowRule> rules, ConnectionMultiplexer redisClient = null, ITimeProvider timeProvider = null, bool updatable = false)
            : base(rules, redisClient, timeProvider, updatable)
        {
            _slidingWindowIncrementLuaScript = new RedisLuaScript(_redisClient, "Src-IncrWithExpireSec",
                                                                  @"local ret={}
                local lock_key=KEYS[1] .. '-lock'
                local lock_val=redis.call('get',lock_key)
                if lock_val == '1' then
                    ret[1]=1
                    ret[2]=-1
                    return ret;
                end
                ret[1]=0
                local st_key=KEYS[1] .. '-st'
                local amount=tonumber(ARGV[1])
                local period_expire_ms=tonumber(ARGV[2])
                local period_ms=tonumber(ARGV[3])
                local period_number=tonumber(ARGV[4])
                local current_time=tonumber(ARGV[5])
                local cal_start_time=tonumber(ARGV[6])
                local limit_number=tonumber(ARGV[7])
                local lock_seconds=tonumber(ARGV[8])
                local current_period
                local current_period_key
                local start_time=redis.call('get',st_key)
                if(start_time==false)
                then
                    start_time=cal_start_time
                    current_period=start_time+period_ms-1
                    current_period_key=KEYS[1] .. '-' .. current_period
                    redis.call('set',st_key,start_time)
                    redis.call('set',current_period_key,amount,'PX',period_expire_ms)
                    ret[2]=amount
                    return ret
                end

                start_time=tonumber(start_time)
                local past_ms=current_time-start_time
                local past_period_number=past_ms/period_ms
                local past_period_number_floor=math.floor(past_period_number)
                local past_period_number_ceil=math.ceil(past_period_number)

                local past_period_number_fixed=past_period_number_floor
                if (past_period_number_ceil > past_period_number_floor)
                then
                    past_period_number_fixed = past_period_number_ceil
                end
                if past_period_number_fixed==0
                then
                    past_period_number_fixed=1
                end
                current_period=start_time + past_period_number_fixed * period_ms - 1
                current_period_key=KEYS[1] .. '-' .. current_period

                local periods={current_period_key}
                for i=1,period_number-1,1 do
                    periods[i+1] = KEYS[1] .. '-' .. (current_period - period_ms * i)
                end
                local periods_amount=0
                local periods_amount_array=redis.call('mget',unpack(periods))
                for key,value in ipairs(periods_amount_array) do
                    if(value~=false)
                    then
                        periods_amount=periods_amount+value
                    end
                end

                ret[2]=amount+periods_amount
                if (limit_number>=0 and ret[2]>limit_number) then
                    if lock_seconds>0 then 
                        redis.call('set',lock_key,'1','EX',lock_seconds,'NX')
                    end
                    ret[1]=1
                    ret[2]=periods_amount
                    return ret
                end

                local current_amount
                current_amount = redis.call('incrby',current_period_key,amount)
                current_amount = tonumber(current_amount)
                if current_amount == amount then
                    redis.call('PEXPIRE',current_period_key,period_expire_ms)
                end

                return ret");
        }
        /// <summary>
        /// create a new instance
        /// </summary>
        /// <param name="rules">The rate limit rules</param>
        /// <param name="redisClient">The redis client</param>
        /// <param name="timeProvider">The time provider</param>
        /// <param name="updatable">If rules can be updated</param>
        public RedisTokenBucketAlgorithm(IEnumerable <TokenBucketRule> rules, ConnectionMultiplexer redisClient = null, ITimeProvider timeProvider = null, bool updatable = false)
            : base(rules, redisClient, timeProvider, updatable)
        {
            _tokenBucketDecrementLuaScript = new RedisLuaScript(_redisClient, "Src-DecrWithTokenBucket",
                                                                @"local ret={}
                local lock_key=KEYS[1] .. '-lock'
                local lock_val=redis.call('get',lock_key)
                if lock_val == '1' then
                    ret[1]=1
                    ret[2]=-1
                    return ret;
                end
                ret[1]=0
                local st_key= KEYS[1] .. '-st'
                local amount=tonumber(ARGV[1])
                local capacity=tonumber(ARGV[2])
                local inflow_unit=tonumber(ARGV[3])
                local inflow_quantity_per_unit=tonumber(ARGV[4])
                local current_time=tonumber(ARGV[5])
                local start_time=tonumber(ARGV[6])
                local lock_seconds=tonumber(ARGV[7])
                local bucket_amount=0
                local last_time=redis.call('get',st_key)
                if(last_time==false)
                then
                    bucket_amount = capacity - amount;
                    redis.call('mset',KEYS[1],bucket_amount,st_key,start_time)
                    ret[2]=bucket_amount
                    return ret
                end
                
                local current_value = redis.call('get',KEYS[1])
                current_value = tonumber(current_value)
                last_time=tonumber(last_time)
                local last_time_changed=0
                local past_time=current_time-last_time
                if(past_time<inflow_unit)
                then
                    bucket_amount=current_value-amount
                else
                    local past_inflow_unit_quantity = past_time/inflow_unit
                    past_inflow_unit_quantity=math.floor(past_inflow_unit_quantity)
                    last_time=last_time+past_inflow_unit_quantity*inflow_unit
                    last_time_changed=1
                    local past_inflow_quantity=past_inflow_unit_quantity*inflow_quantity_per_unit
                    bucket_amount=current_value+past_inflow_quantity-amount
                end

                if(bucket_amount>=capacity)
                then
                    bucket_amount=capacity-amount
                end
                ret[2]=bucket_amount

                if(bucket_amount<0)
                then
                    if lock_seconds>0 then
                        redis.call('set',lock_key,'1','EX',lock_seconds,'NX')
                    end
                    ret[1]=1
                    return ret
                end

                if last_time_changed==1 then
                    redis.call('mset',KEYS[1],bucket_amount,st_key,last_time)
                else
                    redis.call('set',KEYS[1],bucket_amount)
                end
                return ret");
        }
        /// <summary>
        /// create a new instance
        /// </summary>
        /// <param name="rules">The rate limit rules</param>
        /// <param name="redisClient">The redis client</param>
        /// <param name="timeProvider">The time provider</param>
        /// <param name="updatable">If rules can be updated</param>
        public RedisLeakyBucketAlgorithm(IEnumerable <LeakyBucketRule> rules, ConnectionMultiplexer redisClient = null, ITimeProvider timeProvider = null, bool updatable = false)
            : base(rules, redisClient, timeProvider, updatable)
        {
            _leakyBucketIncrementLuaScript = new RedisLuaScript(_redisClient, "Src-IncrWithLeakyBucket",
                                                                @"local ret={}
                local lock_key=KEYS[1] .. '-lock'
                local lock_val=redis.call('get',lock_key)
                if lock_val == '1' then
                    ret[1]=1
                    ret[2]=-1
                    ret[3]=-1
                    return ret;
                end
                ret[1]=0
                local st_key= KEYS[1] .. '-st'
                local amount=tonumber(ARGV[1])
                local capacity=tonumber(ARGV[2])
                local outflow_unit=tonumber(ARGV[3])
                local outflow_quantity_per_unit=tonumber(ARGV[4])
                local current_time=tonumber(ARGV[5])
                local start_time=tonumber(ARGV[6])
                local lock_seconds=tonumber(ARGV[7])
                local last_time=redis.call('get',st_key)
                if(last_time==false)
                then
                    redis.call('mset',KEYS[1],amount,st_key,start_time)
                    ret[2]=0
                    ret[3]=0
                    return ret
                end
                local current_value = redis.call('get',KEYS[1])
                current_value = tonumber(current_value)
                last_time=tonumber(last_time)
                local past_time=current_time-last_time
                local last_time_changed=0
                local wait=0
                if(past_time<outflow_unit)
                then
                    current_value=current_value+amount
                    if(current_value<=capacity+outflow_quantity_per_unit)
                    then
                        local current_unit_rest_time = outflow_unit - past_time
                        if(current_value>outflow_quantity_per_unit)
                        then
                            local batch_number = math.ceil(current_value/outflow_quantity_per_unit) - 1
                            if (batch_number == 1)
                            then
                                wait = current_unit_rest_time;
                            else
                                wait = outflow_unit * (batch_number - 1) + current_unit_rest_time;
                            end
                        end
                    else
                        if lock_seconds>0 then
                            redis.call('set',lock_key,'1','EX',lock_seconds,'NX')
                        end
                        ret[1]=1
                        ret[2]=capacity
                        ret[3]=-1
                        return ret
                    end
                else
                    local past_outflow_unit_quantity = math.floor(past_time/outflow_unit)
                    last_time=last_time+past_outflow_unit_quantity*outflow_unit
                    last_time_changed=1
                    if (current_value < outflow_quantity_per_unit)
                    then
                        current_value = amount
                        wait = 0
                    else
                        local past_outflow_quantity=past_outflow_unit_quantity*outflow_quantity_per_unit
                        local new_value=current_value-past_outflow_quantity+amount
                        if(new_value<=0)
                        then
                            current_value=amount
                        else
                            current_value=new_value
                        end

                        local current_unit_rest_time = outflow_unit - (current_time - last_time)
                        if(current_value>outflow_quantity_per_unit)
                        then
                            local batch_number = math.ceil(current_value/outflow_quantity_per_unit) - 1
                            if (batch_number == 1)
                            then
                                wait = current_unit_rest_time;
                            else
                                wait = outflow_unit * (batch_number - 1) + current_unit_rest_time;
                            end
                        end
                    end
                end

                if last_time_changed==1 then
                    redis.call('mset',KEYS[1],current_value,st_key,last_time)
                else
                    redis.call('set',KEYS[1],current_value)
                end

                local view_count = current_value - outflow_quantity_per_unit;
                if(view_count<0)
                then
                    view_count=0
                end
                ret[2]=view_count
                ret[3]=wait
                return ret");
        }
예제 #7
0
        /// <summary>
        /// Create a new instance
        /// </summary>
        /// <param name="redisClient"></param>
        public RedisStorage(ConnectionMultiplexer redisClient)
        {
            _redisClient = redisClient;

            _fixedWindowIncrementLuaScript = new RedisLuaScript(_redisClient, "Src-IncrWithExpireSec",
                                                                @"local ret={}
                local lock_key=KEYS[1] .. '-lock'
                local lock_val=redis.call('get',lock_key)
                if lock_val == '1' then
                    ret[1]=1
                    ret[2]=-1
                    return ret;
                end
                ret[1]=0
                local amount=tonumber(ARGV[1])
                local limit_number=tonumber(ARGV[3])
                local lock_seconds=tonumber(ARGV[4])
                local check_result=false
                local current
                current = redis.call('get',KEYS[1])
                if current~=false then
                    current = tonumber(current)
                    if current>=limit_number then
                        check_result=true
                    else
                        redis.call('incrby',KEYS[1],amount)
                    end
                else
                    redis.call('set',KEYS[1],amount,'PX',ARGV[2])
                    current=amount
                end
                ret[2]=current
                if check_result then
                    ret[1]=1
                    if lock_seconds>0 then
                        redis.call('set',lock_key,'1','EX',lock_seconds,'NX')
                    end
                end
                return ret");

            _slidingWindowIncrementLuaScript = new RedisLuaScript(_redisClient, "Src-IncrWithExpireSec",
                                                                  @"local ret={}
                local lock_key=KEYS[1] .. '-lock'
                local lock_val=redis.call('get',lock_key)
                if lock_val == '1' then
                    ret[1]=1
                    ret[2]=-1
                    return ret;
                end
                ret[1]=0
                local st_key=KEYS[1] .. '-st'
                local amount=tonumber(ARGV[1])
                local period_expire_ms=tonumber(ARGV[2])*2
                local period_ms=tonumber(ARGV[3])
                local period_number=tonumber(ARGV[4])
                local current_time=tonumber(ARGV[5])
                local limit_number=tonumber(ARGV[6])
                local lock_seconds=tonumber(ARGV[7])
                local current_period
                local current_period_key
                local start_time=redis.call('get',st_key)
                if(start_time==false)
                then
                    start_time=current_time
                    current_period=start_time+period_ms-1
                    current_period_key=KEYS[1] .. '-' .. current_period
                    redis.call('set',st_key,start_time)
                    redis.call('set',current_period_key,amount,'PX',period_expire_ms)
                    ret[2]=amount
                    return ret
                end

                start_time=tonumber(start_time)
                local past_ms=current_time-start_time
                local past_period_number=past_ms/period_ms
                local past_period_number_floor=math.floor(past_period_number)
                local past_period_number_ceiling=math.ceil(past_period_number)

                local past_period_number_fixed=past_period_number_floor
                if (past_period_number_ceiling > past_period_number_floor)
                then
                    past_period_number_fixed = past_period_number_ceiling
                end
                if past_period_number_fixed==0
                then
                    past_period_number_fixed=1
                end
                current_period=start_time + past_period_number_fixed * period_ms - 1
                current_period_key=KEYS[1] .. '-' .. current_period

                local periods={current_period_key}
                for i=1,period_number-1,1 do
                    periods[i+1] = KEYS[1] .. '-' .. (current_period - period_ms * i)
                end
                local periods_amount=0
                local periods_amount_array=redis.call('mget',unpack(periods))
                for key,value in ipairs(periods_amount_array) do
                    if(value~=false)
                    then
                        periods_amount=periods_amount+value
                    end
                end

                ret[2]=amount+periods_amount
                if ret[2] > limit_number then
                    if lock_seconds>0 then 
                        redis.call('set',lock_key,'1','EX',lock_seconds,'NX')
                    end
                    ret[1]=1
                    return ret
                end

                local current_amount
                current_amount = redis.call('incrby',current_period_key,amount)
                current_amount = tonumber(current_amount)
                if current_amount == amount then
                    redis.call('PEXPIRE',current_period_key,period_expire_ms)
                end

                return ret");

            _leakyBucketIncrementLuaScript = new RedisLuaScript(_redisClient, "Src-IncrWithLeakyBucket",
                                                                @"local ret={}
                local lock_key=KEYS[1] .. '-lock'
                local lock_val=redis.call('get',lock_key)
                if lock_val == '1' then
                    ret[1]=1
                    ret[2]=-1
                    return ret;
                end
                ret[1]=0
                local st_key= KEYS[1] .. '-st'
                local amount=tonumber(ARGV[1])
                local capacity=tonumber(ARGV[2])
                local outflow_unit=tonumber(ARGV[3])
                local outflow_quantity_per_unit=tonumber(ARGV[4])
                local current_time=tonumber(ARGV[5])
                local lock_seconds=tonumber(ARGV[6])
                local last_time
                last_time=redis.call('get',st_key)
                if(last_time==false)
                then
                    redis.call('mset',KEYS[1],amount,st_key,current_time)
                    ret[2]=amount
                    return ret
                end
                
                local current_value = redis.call('get',KEYS[1])
                current_value = tonumber(current_value)
                last_time=tonumber(last_time)
                local past_time=current_time-last_time
                local last_time_changed=0
                if(past_time<outflow_unit)
                then
                    current_value=current_value+amount
                else
                    local past_outflow_unit_quantity = past_time/outflow_unit
                    past_outflow_unit_quantity=math.floor(past_outflow_unit_quantity)
                    last_time=last_time+past_outflow_unit_quantity*outflow_unit
                    last_time_changed=1
                    local past_outflow_quantity=past_outflow_unit_quantity*outflow_quantity_per_unit
                    local new_value=current_value-past_outflow_quantity+amount
                    if(new_value<=0)
                    then
                        current_value=amount
                    else
                        current_value=new_value
                    end
                end

                ret[2]=current_value
                if(current_value>capacity)
                then
                    if lock_seconds>0 then
                        redis.call('set',lock_key,'1','EX',lock_seconds,'NX')
                    end
                    ret[1]=1
                    return ret
                end

                if last_time_changed==1 then
                    redis.call('mset',KEYS[1],current_value,st_key,last_time)
                else
                    redis.call('set',KEYS[1],current_value)
                end
                return ret");

            _tokenBucketDecrementLuaScript = new RedisLuaScript(_redisClient, "Src-DecrWithTokenBucket",
                                                                @"local ret={}
                local lock_key=KEYS[1] .. '-lock'
                local lock_val=redis.call('get',lock_key)
                if lock_val == '1' then
                    ret[1]=1
                    ret[2]=-1
                    return ret;
                end
                ret[1]=0
                local st_key= KEYS[1] .. '-st'
                local amount=tonumber(ARGV[1])
                local capacity=tonumber(ARGV[2])
                local inflow_unit=tonumber(ARGV[3])
                local inflow_quantity_per_unit=tonumber(ARGV[4])
                local current_time=tonumber(ARGV[5])
                local lock_seconds=tonumber(ARGV[6])
                local bucket_amount=0
                local last_time=redis.call('get',st_key)
                if(last_time==false)
                then
                    bucket_amount = capacity - amount;
                    redis.call('mset',KEYS[1],bucket_amount,st_key,current_time)
                    ret[2]=bucket_amount
                    return ret
                end
                
                local current_value = redis.call('get',KEYS[1])
                current_value = tonumber(current_value)
                last_time=tonumber(last_time)
                local last_time_changed=0
                local past_time=current_time-last_time
                if(past_time<inflow_unit)
                then
                    bucket_amount=current_value-amount
                else
                    local past_inflow_unit_quantity = past_time/inflow_unit
                    past_inflow_unit_quantity=math.floor(past_inflow_unit_quantity)
                    last_time=last_time+past_inflow_unit_quantity*inflow_unit
                    last_time_changed=1
                    local past_inflow_quantity=past_inflow_unit_quantity*inflow_quantity_per_unit
                    bucket_amount=current_value+past_inflow_quantity-amount
                end

                if(bucket_amount>=capacity)
                then
                    bucket_amount=capacity-amount
                end
                ret[2]=bucket_amount

                if(bucket_amount<0)
                then
                    if lock_seconds>0 then
                        redis.call('set',lock_key,'1','EX',lock_seconds,'NX')
                    end
                    ret[1]=1
                    return ret
                end

                if last_time_changed==1 then
                    redis.call('mset',KEYS[1],bucket_amount,st_key,last_time)
                else
                    redis.call('set',KEYS[1],bucket_amount)
                end
                return ret");
        }