首先,RPermitExpirableSemaphore 是出自於Redisson,Redisson是一個在Redis的基礎上實現的Java駐記憶體資料網格(In-Memory Data Grid),Redisson也是redis官方推薦的,比較常用的是它的分散式鎖。
Redisson中文檔案:
https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95
RPermitExpirableSemaphore ,可過期性號誌
介面檔案:
https://www.javadoc.io/doc/org.redisson/redisson/latest/index.html
舉個栗子
建立RedissonClient 單機版
@Bean
public RedissonClient redissonClient() {
Config config = new Config();
// 單機模式
config.useSingleServer()
.setAddress("redis://127.0.0.1:6379")
;
return Redisson.create(config);
}
一次完整的獲得許可,釋放許可
public void test() throws Exception {
//通過redissonClient創造一個key為xxx的號誌
RPermitExpirableSemaphore semaphore = redissonClient.getPermitExpirableSemaphore("xxx");
//給號誌設定數量為5,用在限流中就是隻允許5次請求
while (!semaphore.trySetPermits(5)) ;
//tryAcquire 的第一個引數是waittime,嘗試獲得許可證的最大等待時間,超過這個時間則返回null
//第二個引數是許可證的過期時間,也是精華所在,即使發生宕機,jvm崩潰等情況,也不用擔心,號誌過期會自動釋放
//成功之後會獲得一個許可證ID,是在獲取期間每次生成的128位元唯一隨機識別符號
String permitId = semaphore.tryAcquire(1, 5, TimeUnit.SECONDS);
System.out.println(permitId);
//釋放許可證還需要之前獲得permitId
semaphore.release(permitId);
}
semaphore.trySetPermits() 中 lua指令碼分析
@Override
public RFuture<Boolean> trySetPermitsAsync(int permits) {
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
"local value = redis.call('get', KEYS[1]); " +
//value==0或者false,都是代表沒有這個號誌,進入建立號誌的邏輯
"if (value == false or value == 0) then "
//所謂建立,其實就是redis的set操作, ARGV[1]即為要設定的許可數
+ "redis.call('set', KEYS[1], ARGV[1]); "
+ "redis.call('publish', KEYS[2], ARGV[1]); "
+ "return 1;"
+ "end;"
+ "return 0;",
Arrays.<Object>asList(getName(), getChannelName()), permits);
}
1.Caused by: org.redisson.client.RedisException: ERR This instance has cluster support disabled. channel: [id: 0x0fd46049, L:/127.0.0.1:62555 - R:/127.0.0.1:6379] command: CommandData [promise=org.redisson.misc.RedissonPromise@74bd3426[Not completed], command=(CLUSTER NODES), params=[], codec=null]
是由於設定了叢集
Config config = new Config();
// set叢集版報錯
config.useClusterServers()
.addNodeAddress("redis://127.0.0.1:6379")
應該為
config.useSingleServer()
.setAddress("redis://127.0.0.1:6379")
2.trySetPermits 一直返回的false,可以檢視下是否redis中有了相同的key,trySetPermits 其實就是往redis中塞值。