主流的四种限流策略,我都可以通过redis实现

「Redis应用」Redis ”高级“应用场景 -- 限流、延时队列、幂等处理 因为可能x已经不在缓存里了,此时又进来了一个y,y的hash结果跟x一模一样,此时会出现误判。 阅读详情

引言

  • 在web开发中功能是基石,除了功能以外运维和防护就是重头戏了。因为在网站运行期间可能会因为突然的访问量导致业务异常、也有可能遭受别人恶意攻击
  • 所以我们的接口需要对流量进行限制。俗称的QPS也是对流量的一种描述
  • 针对限流现在大多应该是令牌桶算法,因为它能保证更多的吞吐量。除了令牌桶算法还有他的前身漏桶算法和简单的计数算法
  • 下面我们来看看这四种算法

固定时间窗口算法

  • 固定时间窗口算法也可以叫做简单计数算法。网上有很多都将计数算法单独抽离出来。但是笔者认为计数算法是一种思想,而固定时间窗口算法是他的一种实现
  • 包括下面滑动时间窗口算法也是计数算法的一种实现。因为计数如果不和时间进行绑定的话那么失去了限流的本质了。就变成了拒绝了

优点

  • 在固定的时间内出现流量溢出可以立即做出限流。每个时间窗口不会相互影响
  • 在时间单元内保障系统的稳定。保障的时间单元内系统的吞吐量上限

缺点

  • 正如图示一样,他的最大问题就是临界状态。在临界状态最坏情况会受到两倍流量请求
  • 除了临界的情况,还有一种是在一个单元时间窗内前期如果很快地消耗完请求阈值。那么剩下的时间将会无法请求。这样就会因为一瞬间的流量导致一段时间内系统不可用。这在互联网高可用的系统中是不能接受的。

实现

  • 好了,关于原理介绍及优缺点我们已经了解了。下面我们动手实现它
  • 首先我们在实现这种计数时,采用redis是非常好的选择。这里我们通过redis实现

controller

@RequestMapping(value = "/start",method = RequestMethod.GET)
    public Map<String,Object> start(@RequestParam Map<String, Object> paramMap) {
        return testService.startQps(paramMap);
    }

service

@Override
public Map<String, Object> startQps(Map<String, Object> paramMap) {
    //根据前端传递的qps上线
    Integer times = 100;
    if (paramMap.containsKey("times")) {
        times = Integer.valueOf(paramMap.get("times").toString());
    }
    String redisKey = "redisQps";
    RedisAtomicInteger redisAtomicInteger = new RedisAtomicInteger(redisKey, redisTemplate.getConnectionFactory());
    int no = redisAtomicInteger.getAndIncrement();
    //设置时间固定时间窗口长度 1S
    if (no == 0) {
        redisAtomicInteger.expire(1, TimeUnit.SECONDS);
    }
    //判断是否超限  time=2 表示qps=3
    if (no > times) {
        throw new RuntimeException("qps refuse request");
    }
    //返回成功告知
    Map<String, Object> map = new HashMap<>();
    map.put("success", "success");
    return map;
}

结果测试

  • 我们设置的qps=3 , 我们可以看到五个并发进来后前三个正常访问,后面两个就失败了。稍等一段时间我们在并发访问,前三个又可以正常访问。说明到了下一个时间窗口

滑动时间窗口算法

  • 针对固定时间窗口的缺点--临界值出现双倍流量问题。 我们的滑动时间窗口就产生了。
  • 其实很好理解,就是针对固定时间窗口,将时间窗口统计从原来的固定间隔变成更加细度化的单元了。
  • 在上面我们固定时间窗口演示中我们设置的时间单元是1S 。 针对1S我们将1S拆成时间戳。
  • 固定时间窗口是统计单元随着时间的推移不断向后进行。而滑动时间窗口是我们认为的想象出一个时间单元按照相对论的思想将时间固定,我们的抽象时间单元自己移动。抽象的时间单元比实际的时间单元更小。
  • 读者可以看下下面的动图,就可以理解了。

优点

  • 实质上就是固定时间窗口算法的改进。所以固定时间窗口的缺点就是他的优点。
  • 内部抽象一个滑动的时间窗,将时间更加小化。存在边界的问题更加小。客户感知更弱了。

缺点

  • 不管是固定时间窗口算法还是滑动时间窗口算法,他们都是基于计数器算法进行优化,但是他们对待限流的策略太粗暴了。
  • 为什么说粗暴呢,未限流他们正常放行。一旦达到限流后就会直接拒绝。这样我们会损失一部分请求。这对于一个产品来说不太友好

实现

  • 滑动时间窗口是将时间更加细化,上面我们是通过redis#setnx实现的。这里我们就无法通过他统一记录了。我们应该加上更小的时间单元存储到一个集合汇总。然后根据集合的总量计算限流。redis的zsett数据结构就和符合我们的需求。
  • 为什么选择zset呢,因为redis的zset中除了值以外还有一个权重。会根据这个权重进行排序。如果我们将我们的时间单元及时间戳作为我们的权重,那么我们获取统计的时候只需要按照一个时间戳范围就可以了。
  • 因为zset内元素是唯一的,所以我们的值采用uuid或者雪花算法一类的id生成器

controller

@RequestMapping(value = "/startList",method = RequestMethod.GET)
    public Map<String,Object> startList(@RequestParam Map<String, Object> paramMap) {
        return testService.startList(paramMap);
    }

service

String 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 也没有出现坏的情况
  • 这里就说明滑动比固定的优处了。如果我们调更小应该更加不会出现临界问题,不过说到底他还是避免不了临界出现的问题

漏桶算法

  • 滑动时间窗口虽然可以极大程度地规避临界值问题,但是始终还是避免不了
  • 另外时间算法还有个致命的问题,他无法面对突如其来的大量流量,因为他在达到限流后直接就拒绝了其他额外流量
  • 针对这个问题我们继续优化我们的限流算法。 漏桶算法应运而生

优点

  • 面对限流更加的柔性,不再粗暴的拒绝。
  • 增加了接口的接收性
  • 保证下流服务接收的稳定性。均匀下发

缺点

  • 我觉得没有缺点。非要鸡蛋里挑骨头那我只能说漏桶容量是个短板

实现

controller

@RequestMapping(value = "/startLoutong",method = RequestMethod.GET)
public Map<String,Object> startLoutong(@RequestParam Map<String, Object> paramMap) {
    return testService.startLoutong(paramMap);
}

service

  • 在service中我们通过redis的list的功能模拟出桶的效果。这里代码是实验室性质的。在真实使用中我们还需要考虑并发的问题
@Override
public 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;
}

下游消费

@Component
public class SchedulerTask {

    @Autowired
    RedisTemplate redisTemplate;

    private String redisKey="qpsList";

    @Scheduled(cron="*/1 * * * * ?")
    private void process(){
        //一次性消费两个
        System.out.println("正在消费。。。。。。");
        redisTemplate.opsForList().trim(redisKey, 2, -1);
    }

}

测试

  • 我们还是通过50并发循环10次访问。我们可以发现只有在一开始能达到比较高的吞吐量。在随后桶的容量满了之后。而下游水滴速率比上游请求速率慢的情况下。只能以下游恒定的速度接收访问。
  • 他的问题也暴露得很明显。针对时间窗口的不足漏桶进行的不足,但是仍是不足。无法彻底避免请求溢出的问题。
  • 请求溢出本身就是一种灾难性的问题。所有的算法目前都没有解决这个问题。只是在减缓他带来的问题

令牌桶算法

  • 令牌桶和漏桶法是一样的。只不过将桶的作用方向改变了一下。
  • 漏桶的出水速度是恒定的,如果流量突然增加的话我们就只能拒绝入池
  • 但是令牌桶是将令牌放入桶中,我们知道正常情况下令牌就是一串字符当桶满了就拒绝令牌的入池,但是面对高流量的时候正常加上我们的超时时间就留下足够长的时间生产及消费令牌了。这样就尽可能地不会造成请求的拒绝
  • 最后,不论是对于令牌桶拿不到令牌被拒绝,还是漏桶的水满了溢出,都是为了保证大部分流量的正常使用,而牺牲掉了少部分流量
public Map<String, Object> startLingpaitong(Map<String, Object> paramMap) {
        String redisKey = "lingpaitong";
        String token = redisTemplate.opsForList().leftPop(redisKey).toString();
        //正常情况需要验证是否合法,防止篡改
        if (StringUtils.isEmpty(token)) {
            throw new RuntimeException("令牌桶拒绝");
        }
        Map<String, Object> map = new HashMap<>();
        map.put("success", "success");
        return map;
    }
@Scheduled(cron="*/1 * * * * ?")
    private void process(){
        //一次性生产两个
        System.out.println("正在消费。。。。。。");
        for (int i = 0; i < 2; i++) {
            redisTemplate.opsForList().rightPush(redisKey, i);
        }
    }
太强了!主流的四种限流策略,都可以通过redis实现 引言 在web开发中功能是基石,除了功能以外运维和防护就是重头菜了。因为在网站运行期间可能会因为突然的访问量导致业务异常、也有可能遭受别人恶意攻击 所以我们的接口需要对流量进行限制。俗称的QPS也是对流量的一种描述 针对限流现在大多应该是令牌桶算法,因为它能保证更多的吞吐量。除了令牌桶算法还有他的前身漏桶算法和简单的计数算法 下面我们来看看这四种算法 当然作为阅读福利,小编精心准备了一套Redis相关学习笔记(包含面试、脑图、手写pdf等)作为福利无条件发送给阅读到本篇文章的Java程序员朋友们,需要的 阅读详情

相关推荐

Java 实现 Redis 5种限流算法

Redis 6.0 引入了新的命令 CL.THROTTLE,这是 Redis 内置的限流功能,可以用来实现令牌桶限流算法。通过 CL.THROTTLE,可以在 Redis 服务器端实现限流逻辑,从而避免了客户端与 Redis 之间的多次交互。Redis 6.0 提供了一个模块 redis-cell,其中包含了 CL.THROTTLE 命令,可以用于实现令牌桶限流算法。每当有请求时,从桶中取出一个令牌,如果桶中没有令牌,则进行限流。// 获取桶的当前状态,如果桶为空,则初始化桶容量和上次漏出时间。

wjianwei666的专栏 793

基于redis实现的四种常见的限流策略

引言 在web开发中功能是基石,除了功能以外运维和防护就是重头菜了。因为在网站运行期间可能会因为突然的访问量导致业务异常、也有可能遭受别人恶意攻击 所以我们的接口需要对流量进行限制。俗称的QPS也是对流量的一种描述 针对限流现在大多应该是令牌桶算法,因为它能保证更多的吞吐量。除了令牌桶算法还有他的前身漏桶算法和简单的计数算法 下面我们来看看这四种算法 固定时间窗口算法 固定时间窗口算法也可以叫做简单计数算法。网上有很多都将计数算法单独抽离出来。但是笔者认为计数算法是一种思想,而固定时间窗口算法是他的一

老男孩的架构路 767

DMA+Timer 生成 PWM 波形错位问题分析与解决

本文深入分析了DMA与Timer协同生成PWM波形时出现的错位问题,揭示了DMA传输启动时机与定时器溢出中断的微妙关系。通过占空比强制清零法和预装载值调校技巧,提供了有效的解决方案,并总结了PWM+DMA配置的最佳实践,帮助开发者避免类似问题。

weixin_29191081的博客 350

限流的4种策略--固定窗口、滑动窗口、漏桶、令牌桶

01 Why 分布式系统中,由于接口API无法控制上游调用方的行为,因此当瞬时请求量突增时,会导致服务器占用过多资源,发生响应速度降低、超时、乃至宕机,甚至引发雪崩造成整个系统不可用。 限流,Rate Limiting,就是对API的请求量进行限制,对于超出限制部分的请求作出快速拒绝、快速失败、丢弃处理,以保证本服务以及下游资源系统的稳定。 哪些原因会带来瞬时请求量突增? 1,热点业务、突发热点数据带来的激增。例如微博热搜的爆点。 2,上游系统的bug导致。 3,恶意的攻击流量。 实现限流的方法很多,..

lzf的博客 4146

Simlink与PX4硬件在环仿真(HIL)实现

Simlink与PX4硬件在环仿真(HIL)实现 介于涉及的知识比较多,这里只是简单的介绍一下,更多内容可以参考阿木实验室《基于matlab模型开发》课程。 硬件在环HIL介绍 simlink与PX4通信实现 硬件在环HIL介绍 为来贯彻万物都可以用数学公式表示,我们以simlink的角度来看我们的无人机,那么UAV可以分为以下的几个模块: 传感器模型 滤波器模块 控制器模...

Zipout的博客 6211

Qt + windows + Linux+QtInstallerFramework打包教程

本文介绍了使用Qt Installer Framework在Windows和Linux系统下打包Qt应用程序的完整教程。Windows部分详细说明了从下载安装框架、配置环境变量、生成release版本,到创建安装包目录结构和修改配置文件的完整流程。Linux部分则重点讲解了环境变量设置、桌面应用配置(.desktop文件)以及两种打包方案:一种使用linuxdeployqt生成AppImage文件,另一种采用类似Windows的安装包方式。教程提供了关键配置文件(config.xml、package.xml

Qt历险记,专注Qt技术,三年医疗设备研发经验,1年机器视觉研发经验,半年柔性传感研发经验。 517

主流的四种限流策略,我都可以通过redis实现,干货仅此一篇

互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!” />[外链图片转存中…(img-Zm2Aukku-1713462641179)][外链图片转存中…(img-JlYwI8eY-1713462641179)][外链图片转存中…(img-HUF9PPay-1713462641179)]《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

2401_84048521的博客 888

搞懂 ABAP 里的 Language Settings:登录语言、文本环境语言与 sy-langu 的真实含义

SAP系统中语言设置并非单一字段决定,而是由登录语言(logon language)和文本环境语言(text environment language)共同控制。登录语言在会话期间固定,而文本环境语言可通过SET LOCALE语句修改,影响文本处理、排序、异常显示等行为。开发中需区分两者:使用CL_ABAP_SYST方法获取登录语言,通过sy-langu或GET LOCALE获取当前文本环境语言。语言环境切换应谨慎处理并尽快恢复,避免影响会话中其他程序。在多语言场景下,正确处理语言设置对确保文本相关功能正常

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1307

什么?主流的四种限流策略,居然都可以通过redis实现

引言 在web开发中功能是基石,除了功能以外运维和防护就是重头戏了。因为在网站运行期间可能会因为突然的访问量导致业务异常、也有可能遭受别人恶意攻击 所以我们的接口需要对流量进行限制。俗称的QPS也是对流量的一种描述 针对限流现在大多应该是令牌桶算法,因为它能保证更多的吞吐量。除了令牌桶算法还有他的前身漏桶算法和简单的计数算法 下面我们来看看这四种算法 固定时间窗口算法 固定时间窗口算法也可以叫做简单计数算法。网上有很多都将计数算法单独抽离出来。但是笔者认为计数算法是一种思想,而固定时间窗口算法是.

wdjnb的博客 143

华为OD机试 新系统真题 【最小代价完成论文评审】

华为OD机试 新系统真题【最小代价完成论文评审】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为OD机试真题【最小代价完成论文评审】多语言题解。

qq_45776114的博客 444

C++11、C++14 和 C++17 标准新特性详解

特性C++11C++14C++17auto类型推导✅范围 for 循环✅Lambda 表达式✅✅✅智能指针✅移动语义✅nullptr✅统一初始化{}✅✅constexpr✅✅✅✅数字分隔符✅结构化绑定✅✅✅文件系统库✅。

h a 6 6 6 c k 为 你解决复杂难题! 442

什么?主流的四种限流策略,居然都可以通过redis实现?

引言 在web开发中功能是基石,除了功能以外运维和防护就是重头戏了。因为在网站运行期间可能会因为突然的访问量导致业务异常、也有可能遭受别人恶意攻击 所以我们的接口需要对流量进行限制。俗称的QPS也是对流量的一种描述 针对限流现在大多应该是令牌桶算法,因为它能保证更多的吞吐量。除了令牌桶算法还有他的前身漏桶算法和简单的计数算法 下面我们来看看这四种算法 固定时间窗口算法 固定时间窗口算法也可以叫做简单计数算法。网上有很多都将计数算法单独抽离出来。但是笔者认..

wdj_yyds的博客 191

不使用工具箱,通过MATLAB纯编程实现lstm网络的训练过程

长短期记忆网络(LSTM,Long Short-Term Memory)是一种时间循环神经网络,是为了解决一般的RNN(循环神经网络)存在的长期依赖问题而专门设计出来的,所有的RNN都具有一种重复神经网络模块的链式形式。在标准RNN中,这个重复的结构模块只有一个非常简单的结构,例如一个tanh层。

平时工作较为繁忙,私信消息一般晚上回复,谢谢大家~~ 1015

主流的四种限流策略,我都可以通过redis实现,干货仅此一篇,真的醉了

正如图示一样,他的最大问题就是临界状态。在临界状态最坏情况会受到两倍流量请求 除了临界的情况,还有一种是在一个单元时间窗内前期如果很快地消耗完请求阈值。那么剩下的时间将会无法请求。这样就会因为一瞬间的流量导致一段时间内系统不可用。这在互联网高可用的系统中是不能接受的。 实现 好了,关于原理介绍及优缺点我们已经了解了。下面我们动手实现它 首先我们在实现这种计数时,采用redis是非常好的选择。这里我们通过redis实现 controller @RequestMapping(value..

m0_61453150的博客 183

UniApp 覆盖安装后首次进入 APP pages.json 不生效的解决方案

通过以上步骤,解决了缓存问题导致的首次加载异常。此方案的关键在于强制更新文件名,从根本上避免旧缓存的干扰。

代码简单说 Vue、JAVA、PHP、Node.js 熟练运用,接口、架构、性能全搞定。 689

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

太强了!主流的四种限流策略,都可以通过redis实现(1)

针对固定时间窗口的缺点–临界值出现双倍流量问题。我们的滑动时间窗口就产生了。其实很好理解,就是针对固定时间窗口,将时间窗口统计从原来的固定间隔变成更加细度化的单元了。在上面我们固定时间窗口演示中我们设置的时间单元是1S。针对1S我们将1S拆成时间戳。固定时间窗口是统计单元随着时间的推移不断向后进行。而滑动时间窗口是我们认为的想象出一个时间单元按照相对论的思想将时间固定,我们的抽象时间单元自己移动。抽象的时间单元比实际的时间单元更小。读者可以看下下面的动图,就可以理解了。优点。

2401_83330193的博客 352

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

限流算法详解(固定窗口、滑动窗口、漏桶、令牌桶、Redis分布式限流)

限流是保护系统的第一道防线。为什么需要限流?令牌桶和漏桶有什么区别?滑动窗口如何实现?Redis如何做分布式限流?理解固定窗口的临界问题、滑动窗口的精确控制、漏桶的匀速输出、令牌桶的突发流量、Redis的Lua脚本限流,才能掌握接口防护的本质。从"接口被刷导致服务崩溃"的故障出发,剖析四大限流算法的核心原理。通过固定窗口的临界突刺问题、滑动窗口的时间分片、漏桶的恒定速率、令牌桶的突发处理、Redis+Lua的分布式限流,揭秘从单机到分布式的限流方案。配合详细代码实现与性能对比,给出限流策略的最佳实践。

比较是偷走幸福的小偷 1219

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

服务限流,我有6种实现方式…

总的来说,要保证系统的抗压能力,限流是一个必不可少的环节,虽然可能会造成某些用户的请求被丢弃,但相比于突发流量造成的系统宕机来说,这些损失一般都在可以接受的范围之内。前面也说过,限流可以结合熔断、降级一起使用,多管齐下,保证服务的可用性与健壮性。

IT_1024的博客 352
上一篇: 数万字总结,建议慢慢看,数据库安全之MongoDB渗透
下一篇: Spring安全的角色和权限源码与教程
JavaShark
博客等级 码龄4年 184粉丝 438原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值