使用RateLimiter完成简单的大流量限流,抢购秒杀限流。 RateLimiter是guava提供的基于令牌桶算法的实现类,可以非常简单的完成限流特技,并且根据系统的实际情况来调整生成token的速率。 通常可应用于抢购限流防止冲垮系统;限制某接口、服务单位时间内的访问量,譬如一些第三方服务会对用户访问量进行限制;限制网速,单位时间内只允许上传下载多少字节等。 下面来看一些简单的实践demo,需要先引入guava的maven依赖。 Demo1.有很多任务,但希望每秒不超过N个
/** * * 有很多个任务,但希望每秒不超过X个,可用此类 */ public class Demo1 { public static void main(String[] args) { //0.5代表一秒最多多少个 RateLimiter rateLimiter = RateLimiter.create(0.5); List<Runnable> tasks = new ArrayList<Runnable>(); for (int i = 0; i < 10; i++) { tasks.add(new UserRequest(i)); } ExecutorService threadPool = Executors.newCachedThreadPool(); for (Runnable runnable : tasks) { System.out.println("等待时间:" + rateLimiter.acquire()); threadPool.execute(runnable); } } private static class UserRequest implements Runnable { private int id; public UserRequest(int id) { this.id = id; } public void run() { System.out.println(id); } } }点击复制代码复制出错复制成功
Demo2.抢购场景限流 如我们预估数据库能承受并发10,超过了可能会造成故障,我们就可以对该请求接口进行限流。
@RestController public class IndexController { @Resource(name = "db") private GoodInfoService goodInfoService; RateLimiter rateLimiter = RateLimiter.create(10); @RequestMapping("/miaosha") public Object miaosha(int count, String code) { System.out.println("等待时间" + rateLimiter.acquire()); if (goodInfoService.update(code, count) > 0) { return "购买成功"; } return "购买失败"; } @RequestMapping("/add") public Object add() { for (int i = 0; i < 100; i++) { GoodInfo goodInfo = new GoodInfo(); goodInfo.setCode("iphone" + i); goodInfo.setAmount(100); goodInfoService.add(goodInfo); } return "添加成功"; } } 点击复制代码复制出错复制成功
Demo3.抢购场景降级
/** * tryAcquire(long timeout, TimeUnit unit) * 从RateLimiter 获取许可如果该许可可以在不超过timeout的时间内获取得到的话, * 或者如果无法在timeout 过期之前获取得到许可的话,那么立即返回false(无需等待) */ @RequestMapping("/buy") public Object miao(int count, String code) { //判断能否在1秒内得到令牌,如果不能则立即返回false,不会阻塞程序 if (!rateLimiter.tryAcquire(1000, TimeUnit.MILLISECONDS)) { System.out.println("短期无法获取令牌,真不幸,排队也瞎排"); return "失败"; } if (goodInfoService.update(code, count) > 0) { System.out.println("购买成功"); return "成功"; } System.out.println("数据不足,失败"); return "失败"; } 点击复制代码复制出错复制成功
rediscell的使用
Redis 4.0 版本中提供的 Redis-Cell 模块,该模块使用的是漏斗算法,并且提供了原子的限流指令,而且依靠 Redis 这个天生的分布式程序就可以实现比较完美的限流了。Redis-Cell 实现限流的方法也很简单,只需要使用一条指令 cl.throttle 即可,使用示例如下:
> cl.throttle mylimit 15 30 60 1)(integer)0 # 0 表示获取成功,1 表示拒绝 2)(integer)15 # 漏斗容量 3)(integer)14 # 漏斗剩余容量 4)(integer)-1 # 被拒绝之后,多长时间之后再试(单位:秒)-1 表示无需重试 5)(integer)2 # 多久之后漏斗完全空出来