在多线程的情况下对一个接口进行访问,如果访问次数过大,且没有缓存存在的情况下大量的请求打到数据库可能会存在数据库宕机,从而造成服务的不可用性。往往我们需要对其进行限流操作用来保证服务的高可用性,以下介绍下redis限流如何使用。
Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
Lua 本身并没有提供对于原子性的直接支持
,它只是一种脚本语言,通常是嵌入到其他宿主程序中运行,比如Redis。 在Redis中,执行Lua脚本的原子性是指:整个Lua脚本在执行期间,不会被其他客户端的命令打断。
lua语法教程
Redis 脚本使用 Lua 解释器来执行脚本。 Redis 2.6 版本通过内嵌支持 Lua 环境。执行脚本的常用命令为
EVAL
。redis Eval 命令基本语法如下:
redis 127.0.0.1:6379> EVAL script numkeys key [key ...] arg [arg ...]
参数说明:
首先,我们定义一个lua脚本:
local key = KEYS[1];
local times = ARGV[1];
local expire = ARGV[2];
local afterval = redis.call('incr',key);
if afterval ==1 then
redis.call('expire',key,tonumber(expire) )
return 1;
end;
if afterval > tonumber(times) then
return 0;
end
return 1;
这个脚本首先定义了三个成员变量用来获取方法中传入的值,redis.call()方法是redis的命令脚本执行,即redis执行incr操作,对key中存储的key值加1操作,如果afterval值等于1时,执行redis的expire,设置key的过期时间,tonumber是将参数值转换为数值,返回,如果加一后的值大于我们传入的规定值时,返回0,进行限流。
java代码实现限流
public boolean acquire(String limitKey, int limit, int expire) {
Jedis jedis = new Jedis("127.0.0.1", 6379);
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setResultType(Long.class);
redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("rateLimiter.lua")));
Long result = (Long) jedis.eval(redisScript.getScriptAsString(), 1, limitKey, String.valueOf(limit), String.valueOf(expire));
if (result == 0){
return false;
}else {
return true;
}
}
controller层调用
if (isAcquire.acquire("myKey",10,60)){
// 接口放行
return "success";
}else {
// 接口拒绝
return "err";
}
这个方法是传入一个你的key,这个key下每一分钟只能请求10次,如果超出10次,进行限流操作,等到上次请求接口的时间超出1分钟后才可以进行放行操作。