- 滑动时间窗口是将时间更加细化,上面我们是通过redis#setnx实现的 。这里我们就无法通过他统一记录了 。我们应该加上更小的时间单元存储到一个集合汇总 。然后根据集合的总量计算限流 。redis的zsett数据结构就和符合我们的需求 。
- 为什么选择zset呢,因为redis的zset中除了值以外还有一个权重 。会根据这个权重进行排序 。如果我们将我们的时间单元及时间戳作为我们的权重,那么我们获取统计的时候只需要按照一个时间戳范围就可以了 。
- 因为zset内元素是唯一的,所以我们的值采用uuid或者雪花算法一类的id生成器
@RequestMapping(value = https://www.isolves.com/it/sjk/Redis/2021-05-31/"/startList",method = RequestMethod.GET)public Map startList(@RequestParam Map paramMap) {return testService.startList(paramMap);}
serviceString redisKey = "qpsZset";Integer times = 100;if (paramMap.containsKey("times")) {times = Integer.valueOf(paramMap.get("times").toString());}long currentTimeMillis = System.currentTimeMillis();long interMills = inter * 1000L;Long count = redisTemplate.opsForZSet().count(redisKey, currentTimeMillis - interMills, currentTimeMillis);if (count > times) {throw new RuntimeException("qps refuse request");}redisTemplate.opsForZSet().add(redisKey, UUID.randomUUID().toString(), currentTimeMillis);Map<String, Object> map = new HashMap<>();map.put("success", "success");return map;
结果测试文章插图
- 和固定时间窗口采用相同的并发 。为什么上面也会出现临界状况呢 。因为在代码里时间单元间隔比固定时间间隔采用还要大。上面演示固定时间窗口时间单元是1S出现了最坏情况 。而滑动时间窗口设计上就应该间隔更短 。而我设置成10S 也没有出现坏的情况
- 这里就说明滑动比固定的优处了 。如果我们调更小应该更加不会出现临界问题,不过说到底他还是避免不了临界出现的问题
- 滑动时间窗口虽然可以极大程度地规避临界值问题,但是始终还是避免不了
- 另外时间算法还有个致命的问题,他无法面对突如其来的大量流量,因为他在达到限流后直接就拒绝了其他额外流量
- 针对这个问题我们继续优化我们的限流算法 。漏桶算法应运而生
文章插图
优点
- 面对限流更加的柔性,不再粗暴的拒绝 。
- 增加了接口的接收性
- 保证下流服务接收的稳定性 。均匀下发
- 我觉得没有缺点 。非要鸡蛋里挑骨头那我只能说漏桶容量是个短板
@RequestMapping(value = https://www.isolves.com/it/sjk/Redis/2021-05-31/"/startLoutong",method = RequestMethod.GET)public Map startLoutong(@RequestParam Map paramMap) {return testService.startLoutong(paramMap);}
service- 在service中我们通过redis的list的功能模拟出桶的效果 。这里代码是实验室性质的 。在真实使用中我们还需要考虑并发的问题
@Overridepublic Map<String, Object> startLoutong(Map<String, Object> paramMap) {String redisKey = "qpsList";Integer times = 100;if (paramMap.containsKey("times")) {times = Integer.valueOf(paramMap.get("times").toString());}Long size = redisTemplate.opsForList().size(redisKey);if (size >= times) {throw new RuntimeException("qps refuse request");}Long aLong = redisTemplate.opsForList().rightPush(redisKey, paramMap);if (aLong > times) {//为了防止并发场景 。这里添加完成之后也要验证 。即使这样本段代码在高并发也有问题 。此处演示作用redisTemplate.opsForList().trim(redisKey, 0, times-1);throw new RuntimeException("qps refuse request");}Map<String, Object> map = new HashMap<>();map.put("success", "success");return map;}
下游消费@Componentpublic class SchedulerTask {@AutowiredRedisTemplate redisTemplate;private String redisKey="qpsList";@Scheduled(cron="*/1 * * * * ?")private void process(){//一次性消费两个System.out.println("正在消费 。。。。。。");redisTemplate.opsForList().trim(redisKey, 2, -1);}}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 固伦和孝公主出生时乾隆多大,固伦温宪公主与雍正关系如何
- 马鞭草酮迷迭香纯露好吗,橙花薰衣草洋甘菊迷迭香四种纯露
- 赤壁之战中大败曹操的主要人物是谁和谁,赤壁之战曹操为何败
- 云南普洱老茶之殇,云南普洱砖茶主要特点
- 开春多吃四种菜 初春饮食的注意事项
- 郑庄公是郑国第二代君主,我认为郑庄公是个什么人
- 诸葛亮死了刘禅当了多少年君主,诸葛亮死后刘禅的结局
- 奥索里奥主战坦克安装哪国的滑膛炮?
- 大太监和硕公主结局,和硕公主嫁给谁了
- 乾隆的固伦公主都有谁,和孝固伦公主是谁的女儿