序
本文主要研究一下redisson的RRateLimiter
RRateLimiter
redisson/src/main/java/org/redisson/api/RRateLimiter.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | public interface RRateLimiter extends RRateLimiterAsync, RExpirable { /** * Initializes RateLimiter's state and stores config to Redis server. * * @param mode - rate mode * @param rate - rate * @param rateInterval - rate time interval * @param rateIntervalUnit - rate time interval unit * @return {@code true} if rate was set and {@code false} * otherwise */ boolean trySetRate(RateType mode, long rate, long rateInterval, RateIntervalUnit rateIntervalUnit); /** * Updates RateLimiter's state and stores config to Redis server. * * @param mode - rate mode * @param rate - rate * @param rateInterval - rate time interval * @param rateIntervalUnit - rate time interval unit */ void setRate(RateType mode, long rate, long rateInterval, RateIntervalUnit rateIntervalUnit); /** * Acquires a permit only if one is available at the * time of invocation. * * <p>Acquires a permit, if one is available and returns immediately, * with the value {@code true}, * reducing the number of available permits by one. * * </p><p>If no permit is available then this method will return * immediately with the value {@code false}. * * @return {@code true} if a permit was acquired and {@code false} * otherwise */ boolean tryAcquire(); /** * Acquires the given number of <code>permits</code> only if all are available at the * time of invocation. * * </p><p>Acquires a permits, if all are available and returns immediately, * with the value {@code true}, * reducing the number of available permits by given number of permits. * * </p><p>If no permits are available then this method will return * immediately with the value {@code false}. * * @param permits the number of permits to acquire * @return {@code true} if a permit was acquired and {@code false} * otherwise */ boolean tryAcquire( long permits); /** * Acquires a permit from this RateLimiter, blocking until one is available. * * </p><p>Acquires a permit, if one is available and returns immediately, * reducing the number of available permits by one. * */ void acquire(); /** * Acquires a specified <code>permits</code> from this RateLimiter, * blocking until one is available. * * </p><p>Acquires the given number of permits, if they are available * and returns immediately, reducing the number of available permits * by the given amount. * * @param permits the number of permits to acquire */ void acquire( long permits); /** * Acquires a permit from this RateLimiter, if one becomes available * within the given waiting time. * * </p><p>Acquires a permit, if one is available and returns immediately, * with the value {@code true}, * reducing the number of available permits by one. * * </p><p>If no permit is available then the current thread becomes * disabled for thread scheduling purposes and lies dormant until * specified waiting time elapses. * * </p><p>If a permit is acquired then the value {@code true} is returned. * * </p><p>If the specified waiting time elapses then the value {@code false} * is returned. If the time is less than or equal to zero, the method * will not wait at all. * * @param timeout the maximum time to wait for a permit * @param unit the time unit of the {@code timeout} argument * @return {@code true} if a permit was acquired and {@code false} * if the waiting time elapsed before a permit was acquired */ boolean tryAcquire( long timeout, TimeUnit unit); /** * Acquires the given number of <code>permits</code> only if all are available * within the given waiting time. * * </p><p>Acquires the given number of permits, if all are available and returns immediately, * with the value {@code true}, reducing the number of available permits by one. * * </p><p>If no permit is available then the current thread becomes * disabled for thread scheduling purposes and lies dormant until * the specified waiting time elapses. * * </p><p>If a permits is acquired then the value {@code true} is returned. * * </p><p>If the specified waiting time elapses then the value {@code false} * is returned. If the time is less than or equal to zero, the method * will not wait at all. * * @param permits amount * @param timeout the maximum time to wait for a permit * @param unit the time unit of the {@code timeout} argument * @return {@code true} if a permit was acquired and {@code false} * if the waiting time elapsed before a permit was acquired */ boolean tryAcquire( long permits, long timeout, TimeUnit unit); /** * Returns current configuration of this RateLimiter object. * * @return config object */ RateLimiterConfig getConfig(); /** * Returns amount of available permits. * * @return number of permits */ long availablePermits(); } </p> |
RRateLimiter继承了RRateLimiterAsync、RExpirable接口,它主要定义了trySetRate、setRate、tryAcquire、acquire、getConfig、availablePermits方法
RRateLimiterAsync
redisson/src/main/java/org/redisson/api/RRateLimiterAsync.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | public interface RRateLimiterAsync extends RExpirableAsync { /** * Initializes RateLimiter's state and stores config to Redis server. * * @param mode - rate mode * @param rate - rate * @param rateInterval - rate time interval * @param rateIntervalUnit - rate time interval unit * @return {@code true} if rate was set and {@code false} * otherwise */ RFuture trySetRateAsync(RateType mode, long rate, long rateInterval, RateIntervalUnit rateIntervalUnit); /** * Acquires a permit only if one is available at the * time of invocation. * * <p>Acquires a permit, if one is available and returns immediately, * with the value {@code true}, * reducing the number of available permits by one. * * </p> <p>If no permit is available then this method will return * immediately with the value {@code false}. * * @return {@code true} if a permit was acquired and {@code false} * otherwise */ RFuture tryAcquireAsync(); /** * Acquires the given number of <code>permits</code> only if all are available at the * time of invocation. * * </p><p>Acquires a permits, if all are available and returns immediately, * with the value {@code true}, * reducing the number of available permits by given number of permits. * * </p> <p>If no permits are available then this method will return * immediately with the value {@code false}. * * @param permits the number of permits to acquire * @return {@code true} if a permit was acquired and {@code false} * otherwise */ RFuture tryAcquireAsync( long permits); /** * Acquires a permit from this RateLimiter, blocking until one is available. * * </p><p>Acquires a permit, if one is available and returns immediately, * reducing the number of available permits by one. * * @return void */ RFuture acquireAsync(); /** * Acquires a specified <code>permits</code> from this RateLimiter, * blocking until one is available. * * </p><p>Acquires the given number of permits, if they are available * and returns immediately, reducing the number of available permits * by the given amount. * * @param permits the number of permits to acquire * @return void */ RFuture acquireAsync( long permits); /** * Acquires a permit from this RateLimiter, if one becomes available * within the given waiting time. * * </p><p>Acquires a permit, if one is available and returns immediately, * with the value {@code true}, * reducing the number of available permits by one. * * </p> <p>If no permit is available then the current thread becomes * disabled for thread scheduling purposes and lies dormant until * specified waiting time elapses. * * </p> <p>If a permit is acquired then the value {@code true} is returned. * * </p> <p>If the specified waiting time elapses then the value {@code false} * is returned. If the time is less than or equal to zero, the method * will not wait at all. * * @param timeout the maximum time to wait for a permit * @param unit the time unit of the {@code timeout} argument * @return {@code true} if a permit was acquired and {@code false} * if the waiting time elapsed before a permit was acquired */ RFuture tryAcquireAsync( long timeout, TimeUnit unit); /** * Acquires the given number of <code>permits</code> only if all are available * within the given waiting time. * * </p><p>Acquires the given number of permits, if all are available and returns immediately, * with the value {@code true}, reducing the number of available permits by one. * * </p> <p>If no permit is available then the current thread becomes * disabled for thread scheduling purposes and lies dormant until * the specified waiting time elapses. * * </p> <p>If a permits is acquired then the value {@code true} is returned. * * </p> <p>If the specified waiting time elapses then the value {@code false} * is returned. If the time is less than or equal to zero, the method * will not wait at all. * * @param permits amount * @param timeout the maximum time to wait for a permit * @param unit the time unit of the {@code timeout} argument * @return {@code true} if a permit was acquired and {@code false} * if the waiting time elapsed before a permit was acquired */ RFuture tryAcquireAsync( long permits, long timeout, TimeUnit unit); /** * Updates RateLimiter's state and stores config to Redis server. * * * @param mode - rate mode * @param rate - rate * @param rateInterval - rate time interval * @param rateIntervalUnit - rate time interval unit * @return {@code true} if rate was set and {@code false} * otherwise */ RFuture setRateAsync(RateType mode, long rate, long rateInterval, RateIntervalUnit rateIntervalUnit); /** * Returns current configuration of this RateLimiter object. * * @return config object */ RFuture getConfigAsync(); /** * Returns amount of available permits. * * @return number of permits */ RFuture availablePermitsAsync(); } </p><p></p><p></p><p></p><p></p><p></p> |
RRateLimiterAsync继承了RExpirableAsync,它是async版本的RRateLimiter,它主要定义了trySetRateAsync、setRateAsync、tryAcquireAsync、acquireAsync、getConfigAsync、availablePermitsAsync方法
RedissonRateLimiter
redisson/src/main/java/org/redisson/RedissonRateLimiter.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | public class RedissonRateLimiter extends RedissonExpirable implements RRateLimiter { //...... @Override public boolean tryAcquire() { return tryAcquire( 1 ); } @Override public RFuture tryAcquireAsync() { return tryAcquireAsync(1L); } @Override public boolean tryAcquire( long permits) { return get(tryAcquireAsync(RedisCommands.EVAL_NULL_BOOLEAN, permits)); } @Override public RFuture tryAcquireAsync( long permits) { return tryAcquireAsync(RedisCommands.EVAL_NULL_BOOLEAN, permits); } @Override public void acquire() { get(acquireAsync()); } @Override public RFuture acquireAsync() { return acquireAsync( 1 ); } @Override public void acquire( long permits) { get(acquireAsync(permits)); } @Override public RFuture acquireAsync( long permits) { CompletionStage f = tryAcquireAsync(permits, - 1 , null ).thenApply(res -> null ); return new CompletableFutureWrapper(f); } @Override public boolean tryAcquire( long timeout, TimeUnit unit) { return get(tryAcquireAsync(timeout, unit)); } @Override public RFuture tryAcquireAsync( long timeout, TimeUnit unit) { return tryAcquireAsync( 1 , timeout, unit); } @Override public boolean tryAcquire( long permits, long timeout, TimeUnit unit) { return get(tryAcquireAsync(permits, timeout, unit)); } } |
RedissonRateLimiter继承了RedissonExpirable,实现了RRateLimiter接口
trySetRate
1 2 3 4 5 6 7 8 9 10 11 | public boolean trySetRate(RateType type, long rate, long rateInterval, RateIntervalUnit unit) { return get(trySetRateAsync(type, rate, rateInterval, unit)); } public RFuture trySetRateAsync(RateType type, long rate, long rateInterval, RateIntervalUnit unit) { return commandExecutor.evalWriteNoRetryAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN, "redis.call('hsetnx', KEYS[1], 'rate', ARGV[1]);" + "redis.call('hsetnx', KEYS[1], 'interval', ARGV[2]);" + "return redis.call('hsetnx', KEYS[1], 'type', ARGV[3]);" , Collections.singletonList(getRawName()), rate, unit.toMillis(rateInterval), type.ordinal()); } |
trySetRate委托给了trySetRateAsync,这里主要是使用hsetnx来设置rate、interval、type三个值
setRate
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public void setRate(RateType type, long rate, long rateInterval, RateIntervalUnit unit) { get(setRateAsync(type, rate, rateInterval, unit)); } public RFuture setRateAsync(RateType type, long rate, long rateInterval, RateIntervalUnit unit) { return commandExecutor.evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN, "local valueName = KEYS[2];" + "local permitsName = KEYS[4];" + "if ARGV[3] == '1' then " + " valueName = KEYS[3];" + " permitsName = KEYS[5];" + "end " + "redis.call('hset', KEYS[1], 'rate', ARGV[1]);" + "redis.call('hset', KEYS[1], 'interval', ARGV[2]);" + "redis.call('hset', KEYS[1], 'type', ARGV[3]);" + "redis.call('del', valueName, permitsName);" , Arrays.asList(getRawName(), getValueName(), getClientValueName(), getPermitsName(), getClientPermitsName()), rate, unit.toMillis(rateInterval), type.ordinal()); } |
setRate委托给了setRateAsync,这里使用hset来写入rate、interval、type三个值,如果存在则覆盖;另外这里删除了valueName、permitsName这两个key
tryAcquire
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | public boolean tryAcquire( long permits) { return get(tryAcquireAsync(RedisCommands.EVAL_NULL_BOOLEAN, permits)); } private RFuture tryAcquireAsync(RedisCommand command, Long value) { byte [] random = getServiceManager().generateIdArray(); return commandExecutor.evalWriteAsync(getRawName(), LongCodec.INSTANCE, command, "local rate = redis.call('hget', KEYS[1], 'rate');" + "local interval = redis.call('hget', KEYS[1], 'interval');" + "local type = redis.call('hget', KEYS[1], 'type');" + "assert(rate ~= false and interval ~= false and type ~= false, 'RateLimiter is not initialized')" + "local valueName = KEYS[2];" + "local permitsName = KEYS[4];" + "if type == '1' then " + "valueName = KEYS[3];" + "permitsName = KEYS[5];" + "end;" + "assert(tonumber(rate) >= tonumber(ARGV[1]), 'Requested permits amount could not exceed defined rate'); " + "local currentValue = redis.call('get', valueName); " + "local res;" + "if currentValue ~= false then " + "local expiredValues = redis.call('zrangebyscore', permitsName, 0, tonumber(ARGV[2]) - interval); " + "local released = 0; " + "for i, v in ipairs(expiredValues) do " + "local random, permits = struct.unpack('Bc0I', v);" + "released = released + permits;" + "end; " + "if released > 0 then " + "redis.call('zremrangebyscore', permitsName, 0, tonumber(ARGV[2]) - interval); " + "if tonumber(currentValue) + released > tonumber(rate) then " + "currentValue = tonumber(rate) - redis.call('zcard', permitsName); " + "else " + "currentValue = tonumber(currentValue) + released; " + "end; " + "redis.call('set', valueName, currentValue);" + "end;" + "if tonumber(currentValue) 0 then " + "redis.call('pexpire', valueName, ttl); " + "redis.call('pexpire', permitsName, ttl); " + "end; " + "return res;" , Arrays.asList(getRawName(), getValueName(), getClientValueName(), getPermitsName(), getClientPermitsName()), value, System.currentTimeMillis(), random); } |
tryAcquire委托给了tryAcquireAsync,它通过一个lua脚本来执行,首先通过hget获取rate、interval、type的值,然后根据type来确定valueName、permitsName,如果type为0则valueName是getValueName(),permitsName是getPermitsName(),如果type=1则valueName是getClientValueName(),permitsName是getClientPermitsName();之后获取valueName的值,若为false则直接用设置rate、permits,并递减valueName;若为true则获取expiredValues计算released值,再计算出currentValue,若不够扣则计算返回值,若够扣则通过zadd添加当前permit(
System.currentTimeMillis()
),然后递减valueName
acquire
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | public void acquire() { get(acquireAsync()); } public RFuture acquireAsync() { return acquireAsync( 1 ); } public RFuture acquireAsync( long permits) { CompletionStage f = tryAcquireAsync(permits, - 1 , null ).thenApply(res -> null ); return new CompletableFutureWrapper(f); } public RFuture tryAcquireAsync( long permits, long timeout, TimeUnit unit) { long timeoutInMillis = - 1 ; if (timeout >= 0 ) { timeoutInMillis = unit.toMillis(timeout); } CompletableFuture f = tryAcquireAsync(permits, timeoutInMillis); return new CompletableFutureWrapper(f); } private CompletableFuture tryAcquireAsync( long permits, long timeoutInMillis) { long s = System.currentTimeMillis(); RFuture future = tryAcquireAsync(RedisCommands.EVAL_LONG, permits); return future.thenCompose(delay -> { if (delay == null ) { return CompletableFuture.completedFuture( true ); } if (timeoutInMillis == - 1 ) { CompletableFuture f = new CompletableFuture(); getServiceManager().getGroup().schedule(() -> { CompletableFuture r = tryAcquireAsync(permits, timeoutInMillis); commandExecutor.transfer(r, f); }, delay, TimeUnit.MILLISECONDS); return f; } long el = System.currentTimeMillis() - s; long remains = timeoutInMillis - el; if (remains f = new CompletableFuture(); if (remains { f.complete( false ); }, remains, TimeUnit.MILLISECONDS); } else { long start = System.currentTimeMillis(); getServiceManager().getGroup().schedule(() -> { long elapsed = System.currentTimeMillis() - start; if (remains r = tryAcquireAsync(permits, remains - elapsed); commandExecutor.transfer(r, f); }, delay, TimeUnit.MILLISECONDS); } return f; }).toCompletableFuture(); } |
acquire也是复用了tryAcquireAsync方法,只获取不到时会根据返回的delay进行重新调度,若timeoutInMillis不为-1则会根据超时时间进行计算和重新调度
availablePermits
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | public long availablePermits() { return get(availablePermitsAsync()); } public RFuture availablePermitsAsync() { return commandExecutor.evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_LONG, "local rate = redis.call('hget', KEYS[1], 'rate');" + "local interval = redis.call('hget', KEYS[1], 'interval');" + "local type = redis.call('hget', KEYS[1], 'type');" + "assert(rate ~= false and interval ~= false and type ~= false, 'RateLimiter is not initialized')" + "local valueName = KEYS[2];" + "local permitsName = KEYS[4];" + "if type == '1' then " + "valueName = KEYS[3];" + "permitsName = KEYS[5];" + "end;" + "local currentValue = redis.call('get', valueName); " + "if currentValue == false then " + "redis.call('set', valueName, rate); " + "return rate; " + "else " + "local expiredValues = redis.call('zrangebyscore', permitsName, 0, tonumber(ARGV[1]) - interval); " + "local released = 0; " + "for i, v in ipairs(expiredValues) do " + "local random, permits = struct.unpack('Bc0I', v);" + "released = released + permits;" + "end; " + "if released > 0 then " + "redis.call('zremrangebyscore', permitsName, 0, tonumber(ARGV[1]) - interval); " + "currentValue = tonumber(currentValue) + released; " + "redis.call('set', valueName, currentValue);" + "end;" + "return currentValue; " + "end;" , Arrays.asList(getRawName(), getValueName(), getClientValueName(), getPermitsName(), getClientPermitsName()), System.currentTimeMillis()); } |
availablePermits委托给了availablePermitsAsync,它执行lua脚本,先通过hget获取rate、interval、type的值,然后根据type来确定valueName、permitsName,如果type为0则valueName是getValueName(),permitsName是getPermitsName(),如果type=1则valueName是getClientValueName(),permitsName是getClientPermitsName();之后获取valueName对应的值currentValue,若值为false则重新设置rate,否则通过expiredValues重新计算released,若released大于0则更新到currentValue,最后返回currentValue
小结
redisson的RRateLimiter提供了trySetRate、setRate、tryAcquire、acquire、getConfig、availablePermits方法
- 其RateType有OVERALL(
值为0
)、PER_CLIENT(值为1
)两个类型,如果type为0则valueName是getValueName(),permitsName是getPermitsName(),如果type=1则valueName是getClientValueName(),permitsName是getClientPermitsName() - 它主要定义了几个key,一个是getRawName,类型为hash,其key有rate、interval、type;一个是key为valueName,存储了当前的permits;一个是key为permitsName,类型是sorted set,其score为System.currentTimeMillis(),value通过struct.pack了随机数长度、随机数、此次permit的value
- trySetRate委托给了trySetRateAsync,这里主要是使用hsetnx来设置rate、interval、type三个值;setRate委托给了setRateAsync,这里使用hset来写入rate、interval、type三个值,如果存在则覆盖;另外这里删除了valueName、permitsName这两个key
- tryAcquire委托给了tryAcquireAsync,它通过一个lua脚本来执行,首先通过hget获取rate、interval、type的值,之后获取valueName的值,若为false则直接用设置rate、permits,并递减valueName;若为true则获取expiredValues计算released值,再计算出currentValue,若不够扣则计算返回值(告诉调用方可以延时多长时间再重试),若够扣则通过zadd添加当前permit(
System.currentTimeMillis()
),然后递减valueName - acquire是也是复用了tryAcquireAsync方法,只获取不到时会根据返回的delay进行重新调度,若timeoutInMillis不为-1则会根据超时时间进行计算和重新调度
到此这篇关于redisson中RRateLimiter分布式限流器的使用的文章就介绍到这了,更多相关redisson RRateLimiter内容请搜索IT俱乐部以前的文章或继续浏览下面的相关文章希望大家以后多多支持IT俱乐部!