這里記錄一下工作中常用的lua腳本
主要是用來操作redis,保證多個命令原子性
分布式鎖
主要是用來保證同一個資源在多個服務(wù)中也能保證唯一性的操作
這里加鎖主要是利用setnx 命令咽袜,保證唯一key只能被一個服務(wù)設(shè)置成功宣蔚,并且為了防止出現(xiàn)不能釋放鎖的問題养涮,所以設(shè)置一個過期時間,當(dāng)然如果redis版本比較的高的話蒿赢,一個命令也能支持了
解鎖主要是先get 保證自己仍然持有這把鎖,然后delete釋放這把鎖毛嫉,保證原子性即可
//加鎖解鎖 start
//lock script
private static final String lockScript = " if redis.call('setnx',KEYS[1],ARGV[1]) == 1 " +
" then redis.call('expire',KEYS[1],ARGV[2]) " +
" return 1 " +
" else return 0 end ";
/**
* 加鎖
* @param key
* @param value
* @param second 鎖過期時間
* @return
*/
public boolean lock(String key,String value,Long second){
DefaultRedisScript<Boolean> script = new DefaultRedisScript<>();
script.setScriptText(lockScript);
script.setResultType(Boolean.class);
return stringRedisTemplate.execute(script,Collections.singletonList(key),value,second+"");
}
//unlock script
private static final String unlockScript = " if redis.call('get',KEYS[1]) == ARGV[1] " +
" then return redis.call('del',KEYS[1]) " +
" else return 0 end ";
/**
* 解鎖
* @param key
* @param value
* @return
*/
public boolean unlock(String key,String value){
DefaultRedisScript<Boolean> script = new DefaultRedisScript<>();
script.setScriptText(unlockScript);
script.setResultType(Boolean.class);
return stringRedisTemplate.execute(script,Collections.singletonList(key),value);
}
接口限流
接口限流有很多方案雌桑,
1,從配置上來說务荆,可以在網(wǎng)關(guān)層做妆距,比如ng的配置,也可以在web服務(wù)器上做函匕,比如tomcat的相關(guān)配置
2毅厚,從算法來說,比如令牌浦箱,漏桶等等
這里咱們的需求是針對分布式環(huán)境下吸耿,單個用戶對某個接口的訪問進行限流
需求:對某接口每分鐘請求數(shù)不超過60次(單個用戶60Qps)
這里我們簡單一點,使用redis的zset
偽代碼如下:
以用戶id為key,每次請求先判斷指定時間范圍內(nèi)改用戶的請求記錄 (zcount min max)酷窥,如果大于等于60咽安,則直接return掉;如果小于60,則 zadd key value 當(dāng)前時間戳
這里只要將這兩步操縱合并到同一個腳本中即可蓬推。
代碼如下:
private static final String scripts = "if redis.call('zcount',KEYS[1],ARGV[1],ARGV[2] ) < tonumber(ARGV[3]) " +
"then return redis.call('zadd',KEYS[1],ARGV[4],ARGV[5]) " +
"else return 0 " +
"end";
/**
*
* @param userKey
* @param min
* @param max
* @param maxV
* @param value
* @param score
* @return
*/
public boolean getToken(String userKey,String min,String max,String maxV,String value,String score){
DefaultRedisScript<Boolean> script = new DefaultRedisScript<>();
script.setScriptText(scripts);
script.setResultType(Boolean.class);
return stringRedisTemplate.execute(script,Collections.singletonList(userKey),min,max,maxV,value,score);
}