Lua脚本
Redis从 2.6.0 版本开始内置 Lua 解释器,可以使用 EVAL 命令对 Lua 脚本进行求值。脚本是lua函数体内的代码,而不能是包括function关键字及大括号。脚本会以原子方式执行,当脚本运行时,其他任何客户端都不能执行命令,因此,脚本中代码不应该包含耗时操作。
语法:EVAL script numkeys key [key …] arg [arg …]
。例如:
> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
1) "key1"
2) "key2"
3) "first"
4) "second"
其中 "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}"
是被求值的 Lua 脚本,数字 2
指定了键名参数的数量, key1
和 key2
是键名参数,分别使用 KEYS[1]
和 KEYS[2]
访问,而最后的 first
和 second
则是附加参数,可以通过 ARGV[1]
和 ARGV[2]
访问它们。在脚本中调用Redis命令:
> eval "return redis.call('set',KEYS[1],'bar')" 1 foo
OK
在spring data redis中使用
实现一个简单的计数限流器。
@Configuration
public class RedisConfiguration {
@Bean(name = "simpleRateLimitScript")
public RedisScript<Boolean> script() {
ClassPathResource resource = new ClassPathResource("META-INF/scripts/simple_rate_limiter.lua");
return RedisScript.of(resource, Boolean.class);
}
}
-- 在一段时间内(exp,秒),对某一操作(key),限制允许执行多少次(limit)。
local key = KEYS[1]
local limit = tonumber(KEYS[2])
local exp = tonumber(KEYS[3])
local currentLimit = tonumber(redis.call('GET', key) or 0)
if currentLimit + 1 > limit then
return true
else
redis.call("INCR", key)
redis.call("EXPIRE", key, exp)
return false
end
private static final int MAX = 5;
private static final int EXP_SECONDS = 10 * 60;
public Boolean limit(String key) {
List<String> keys = new ArrayList<>();
keys.add("limit:" + key);
keys.add(String.valueOf(MAX));
keys.add(String.valueOf(EXP_SECONDS));
return stringRedisTemplate.execute(countLimitScript, keys);
}