我用Redis实践了三种实现方法,可以简单地实现。
第一种:基于Redis的setnx操作
当我们使用Redis分布式锁时,我们都知道我们在CAS中依靠Setnx的指令(Compare and swap)在操作过程中,指定的key设置了过期实践(expire),限流的主要目的是在单位时间内,只有N个请求可以访问我的代码程序。因此,依靠setnx可以很容易地实现这方面的功能。
例如,我们需要在10秒内限制20个请求,因此我们可以在setnx中设置10个过期时间,当setnx的数量达到20时,我们可以达到流量限制效果。如果代码相对简单,则不显示。
当然,这种做法有很多缺点。例如,当统计1-10秒时,不能在2-11秒内统计。如果需要在N秒内统计M请求,则需要在我们的Redis中保持Nkey等
二是基于Redis的数据结构zsetet
事实上,限流涉及的最重要的是滑动窗口,上面还提到了1-10是如何变成2-11的。事实上,起始值和终端值都是+1。
如果我们使用Redislist数据结构,我们可以很容易地实现这个功能
我们可以将请求构建成一个zset数组。当每个请求进来时,value是唯一的,可以用UUUID生成,score可以用当前时间戳表示,因为score可以用来计算当前时间戳中的请求数量。zset数据结构还提供了range方法,让我们在两个时间戳中轻松获得多少请求。
代码如下
public Response limitFlow(){ Long currentTime = new Date().getTime(); System.out.println(currentTime); if(redisTemplate.hasKey("limit")) { Integer count = redisTemplate.opsForZSet().rangeByScore("limit", currentTime - intervalTime, currentTime).size(); // intervaltime是限流时间 System.out.println(count); if (count != null && count > 5) { return Response.ok(“每分钟最多只能访问5次”; } } redisTemplate.opsForZSet().add("limit",UUID.randomUUID().toString(),currentTime); return Response.ok(访问成功); }
滑动窗口的效果可以通过上述代码来实现,M请求最多可以在每N秒内保证。缺点是zset的数据结构会越来越大。实现方法相对简单。
第三种:基于Redis的令牌桶算法
说到限流,不得不提到令牌桶算法。
令牌桶算法提到输入速率和输出速率,当输出速率大于输入速率时,就超过了流量限制。
也就是说,每次我们访问请求时,我们都可以从Redis中获得令牌。如果我们获得令牌,这意味着没有超过限制。如果我们得不到它,结果是相反的。
依靠上述思想,我们可以很容易地结合Redis的List数据结构来实现这样的代码,只是简单地实现
依靠ListleftPop获取令牌
// publicicc输出令牌 Response limitFlow2(Long id){ Object result = redisTemplate.opsForList().leftPop("limit_list"); if(result == null){ return Response.ok(“当前令牌桶中无令牌”; } return Response.ok(articledescription2); }
然后依靠Java的定时任务,定期到Listrightpush令牌。当然,令牌也需要独特性,所以我在这里生成了UUID
// 10S的速率将UUID添加到令牌桶中,以确保唯一性 @Scheduled(fixedDelay = 10_000,initialDelay = 0) public void setIntervalTimeTask(){ redisTemplate.opsForList().rightPush("limit_list",UUID.randomUUID().toString()); }
综上所述,代码一开始并不难实现。对于这些限流方式,我们可以将上述代码添加到AOP或filter中,以实现界面的限流,最终保护您的网站。