在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
一、简介 1)分布式限流 如果是单实例项目,我们使用Guava这样的轻便又高性能的堆缓存来处理限流。但是当项目发展为多实例了以后呢?这时候我们就需要采用分布式限流的方式,分布式限流可以以redis + lua 或者 nignx + lua这样的组合来实现。。 分布式限流一般应用场景都是在业务上进行限流,所以本文不涉及niginx + lua,简单介绍redis + lua分布式限流的实现。如果是需要在接入层限流的话,应该直接采用nginx自带的连接数限流模块和请求限流模块。
2)redis redis是一种键值对的单线程架构模型,所以它是线程安全的,也是分布式缓存常用的解决方案。(本文不涉及redis的分布式缓存,只是讲如何结合redis实现限流) 3)lua lua是基于c语言的一种脚本语言,它可以很轻便地被使用在嵌入式方面。我们不会去重写redis,但是我们可以去使用lua来扩展redis的功能。而redis也内置了对lua支持的模块。
二、示例 示例图
1)启动redis服务 我们得确保redis安装,并使用: ./redis-server 命令启动redis服务端 redis常用命令: ./redis-server 启动服务端 ./redis-cli 启动客户端 ./redis-cli shutdown 关闭服务 keys * 查看所有key get 键 根据键获取值 2)编写lua脚本 创建limit.lua文件 local key = KEYS[1] --限流KEY(一秒一个) local limit = tonumber(ARGV[1]) --限流大小 local current = tonumber(redis.call('get', key) or "0") if current + 1 > limit then --如果超出限流大小 return 0 else --请求数+1,并设置2秒过期 redis.call("INCRBY", key,"1") redis.call("expire", key,"1") return 1 end 1)我们通过KEYS[1] 获取传入的key参数 2)通过ARGV[1]获取传入的limit参数 3)redis.call方法,从缓存中get和key相关的值,如果为nil那么就返回0 4)接着判断缓存中记录的数值是否会大于限制大小,如果超出表示该被限流,返回0 5)如果未超过,那么该key的缓存值+1,并设置过期时间为1秒钟以后,并返回1
3)Java调用 java采用jedis来操作redis 引入redis依赖和apache io的工具包(方便读取lua文件的内容) <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.9.0</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.4</version> </dependency> 创建RedisLua.java文件 import org.apache.commons.io.FileUtils; import redis.clients.jedis.Jedis; import java.io.File; import java.io.IOException; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; /** * @author lay * @date 2018/5/22. * @time 17:48 */ public class RedisLua { public static void main(String[] args) { final CountDownLatch latch = new CountDownLatch(1); for (int i = 0; i < 7; i++) { new Thread(new Runnable() { public void run() { try { latch.await(); System.out.println(accquire()); } catch (Exception e) { e.printStackTrace(); } } }).start(); } latch.countDown(); } public static boolean accquire() throws IOException, URISyntaxException { Jedis jedis = new Jedis("127.0.0.1"); File luaFile = new File(RedisLua.class.getResource("/").toURI().getPath() + "limit.lua"); String luaScript = FileUtils.readFileToString(luaFile); String key = "ip:" + System.currentTimeMillis()/1000; // 当前秒 String limit = "5"; // 最大限制 List<String> keys = new ArrayList<String>(); keys.add(key); List<String> args = new ArrayList<String>(); args.add(limit); Long result = (Long)(jedis.eval(luaScript, keys, args)); // 执行lua脚本,传入参数 return result == 1; } } 以上模拟了多线程下,使用jedis连接redis服务,并执行lua脚本
4)输出结果 true
true
false
true
false
true
true
我们看到,7个线程,其中2个线程被判断为false
参考文章:http://jinnianshilongnian.iteye.com/blog/2305117 |
请发表评论