这里记录一下工作中常用的lua脚本
主要是用来操作redis,保证多个命令原子性
分布式锁
主要是用来保证同一个资源在多个服务中也能保证唯一性的操作
这里加锁主要是利用setnx 命令,保证唯一key只能被一个服务设置成功,并且为了防止出现不能释放锁的问题,所以设置一个过期时间,当然如果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,从配置上来说,可以在网关层做,比如ng的配置,也可以在web服务器上做,比如tomcat的相关配置
2,从算法来说,比如令牌,漏桶等等
这里咱们的需求是针对分布式环境下,单个用户对某个接口的访问进行限流
需求:对某接口每分钟请求数不超过60次(单个用户60Qps)
这里我们简单一点,使用redis的zset
伪代码如下:
以用户id为key,每次请求先判断指定时间范围内改用户的请求记录 (zcount min max),如果大于等于60,则直接return掉;如果小于60,则 zadd key value 当前时间戳
这里只要将这两步操纵合并到同一个脚本中即可。
代码如下:
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);
}