本文摘自PHP中文网,作者V,侵删。
目的:
(学习视频分享:redis视频教程)
非脚本实现
1 2 3 | 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
1 2 3 4 5 6 7 8 9 | --
-- 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
1 | 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 嵌入 Redis 优势:
减少网络开销: 不使用 Lua 的代码需要向 Redis 发送多次请求, 而脚本只需一次即可, 减少网络传输;原子操作: Redis 将整个脚本作为一个原子执行, 无需担心并发, 也就无需事务;复用: 脚本会永久保存 Redis 中, 其他客户端可继续使用。
相关推荐:redis数据库教程
以上就是redis可以采用什么方式实现限流的详细内容,更多文章请关注木庄网络博客!
相关阅读 >>
Redis两种持久化方式的缺陷介绍
Redis为什么是单线程
Redis读写分离与哨兵机制配置
Redis怎么清除缓存
Redis为什么是安全的
Redis适合什么场景
Redis除了缓存还能做什么
Redis cluster集群介绍
为什么需要Redis
Redis和rabbitmq区别
更多相关阅读请进入《Redis》频道 >>
机械工业出版社
本书主要讲述了数据模型、基于对象的数据库和XML、数据存储和查询、事务管理、体系结构等方面的内容。
转载请注明出处:木庄网络博客 » redis可以采用什么方式实现限流