目的:
实现访问频率限制
实现访问者 $ip 在一定的时间 $time 内只能访问 $limit 次
(学习视频分享:redis视频教程)
非脚本实现
private boolean accesslimit(string ip, int limit, int time, jedis jedis) { boolean result = true; string key = "rate.limit:" + ip; if (jedis.exists(key)) { long aftervalue = jedis.incr(key); if (aftervalue > limit) { result = false; } } else { transaction transaction = jedis.multi(); transaction.incr(key); transaction.expire(key, time); transaction.exec(); } return result; }
以上代码有两点缺陷
可能会出现竞态条件: 解决方法是用 watch 监控 rate.limit:$ip 的变动, 但较为麻烦;以上代码在不使用 pipeline 的情况下最多需要向redis请求5条指令, 传输过多.
lua脚本实现
redis 允许将 lua 脚本传到 redis 服务器中执行, 脚本内可以调用大部分 redis 命令, 且 redis 保证脚本的原子性:
首先需要准备lua代码: script.lua
---- created by intellij idea.-- user: jifang-- date: 16/8/24-- time: 下午6:11 -- local key = "rate.limit:" .. keys[1] local limit = tonumber(argv[1]) local expire_time = argv[2] local is_exists = redis.call("exists", key) if is_exists == 1 then if redis.call("incr", key) > limit then return 0 else return 1 end else redis.call("set", key, 1) redis.call("expire", key, expire_time) return 1 end
java
private boolean accesslimit(string ip, int limit, int timeout, jedis connection) throws ioexception { list<string> keys = collections.singletonlist(ip); list<string> argv = arrays.aslist(string.valueof(limit), string.valueof(timeout)); return 1 == (long) connection.eval(loadscriptstring("script.lua"), keys, argv); } // 加载lua代码 private string loadscriptstring(string filename) throws ioexception { reader reader = new inputstreamreader(client.class.getclassloader().getresourceasstream(filename)); return charstreams.tostring(reader); }
lua 嵌入 redis 优势:
减少网络开销: 不使用 lua 的代码需要向 redis 发送多次请求, 而脚本只需一次即可, 减少网络传输;原子操作: redis 将整个脚本作为一个原子执行, 无需担心并发, 也就无需事务;复用: 脚本会永久保存 redis 中, 其他客户端可继续使用。
相关推荐:redis数据库教程
以上就是redis可以采用什么方式实现限流的详细内容。