redis实现限流的常见策略

背景

前言

限流算法在分布式领域是一个经常被提起的话题,当系统的处理能力有限时, 如何阻止计划外的请求继续对系统施压,这是一个需要重视的问题。

如果没有限流机制,一旦外部请求超过系统承载的压力,就会出现系统宕机等严重问题。

加入限流正是为了保证系统负载在可以承受的范围内。

 实际案例分析

比如双11的秒杀环节,我们在上线前预估了能应对的秒杀qps是 100w/s,但是实际可能达到了1000w/s,这种情况下这多出来的900w请求很可能压垮我们的数据库,甚至可能会导致集群雪崩,进而影响到接下来所有的用户正常访问。

常见的限流策略介绍

固定时间窗口算法
滑动时间窗口算法
漏桶算法
令牌桶算法

一、固定时间窗口算法

固定窗口算法又叫计数器算法,是一种简单方便的限流算法。
主要通过一个支持原子操作的计数器来累计n秒内的请求次数,当n秒内计数达到限流阈值时触发拒绝策略。
每过n秒,计数器重置为 0 开始重新计数。

 ​​​​​​

优点 

1、实现和理解起来比较简单,时间窗口是固定和隔离的,每个时间窗口都互不影响,性能也比较高。

缺点

1、正如图示一样,他的最大问题就是临界状态,在临界状态最坏情况会受到两倍流量请求。

2、还有一种是在一个单元时间窗内前期如果很快的消耗完请求阈值。那么剩下的时间将会无法请求。这样就会因为一瞬间的流量导致一段时间内系统不可用。这在互联网高可用的系统中是不能接受的。

二、滑动时间窗口算法

滑动窗口为固定窗口的改良版,解决了固定窗口在窗口切换时会受到两倍于阈值数量的请求,滑动窗口在固定窗口的基础上,将一个窗口分为若干个等份的小窗口,每个小窗口对应不同的时间点,拥有独立的计数器,当请求的时间点大于当前窗口的最大时间点时,则将窗口向前平移一个小窗口(将第一个小窗口的数据舍弃,第二个小窗口变成第一个小窗口,当前请求放在最后一个小窗口),整个窗口的所有请求数相加不能大于阈值。其中,Sentinel就是采用滑动窗口算法来实现限流的

  1.  把3秒钟划分为3个小窗,每个小窗限制请求不能超过50秒。
  2. 比如我们设置,3秒内不能超过150个请求,那么这个窗口就可以容纳3个小窗,并且随着时间推移,往前滑动。每次请求过来后,都要统计滑动窗口内所有小窗的请求总量。
优点 

1、优化了固定时间窗口的临界问题,限流更加精准(窗口拆分的越小限流越精准)。

缺点

1、实现较为复杂,会占用较多内存,每个请求都需要重新统计最新时间窗口内的请求数,性能较低。

2、无法应对短时间高并发(突刺现象)。

三、漏桶算法

漏桶限流算法的核心就是:不管上面的水流速度有多块,漏桶水滴的流出速度始终保持不变。

假设限流要求是每分钟允许3w个请求,可以将漏桶的容积看作3w,每次请求加水量为1,漏桶中水的流出速度为3w/1分钟。
当出现最大3w个并发时,漏桶会被瞬间注满水,后续请求都会被拒绝。只有随着水量以固定速度流出后,漏斗中有剩余空间容纳新的水量,系统才能接受的新的请求。

优点 

1、面对限流更加的柔性,不在粗暴的拒绝。

2、增加了接口的接收性。

3、保证下流服务接收的稳定性,均匀下发。

缺点

1、资源利用率低:漏桶并不能高效地利用可用的资源。因为它只在固定的时间间隔放行请求,所以在很多情况下,流量非常低,即使不存在资源争用,也无法有效地消耗资源。

2、饥饿问题:当短时间内有大量突发请求,即使服务器没有任何负载,由于漏桶中的水还没有流出,请求会大量被拒绝。

四、令牌桶算法

令牌桶是网络流量整形(Traffic Shaping)和速率限制(Rate Limiting)中最常使用的一种算法。对于每一个请求,都需要从令牌桶中获得一个令牌;如果没有获得令牌,则需要触发限流策略。系统会以恒定速度(r tokens/sec)往固定容量的令牌桶中放入令牌。令牌桶有固定的大小,如果令牌桶被填满,则会丢弃令牌。

 会存在三种情况:

请求速度 >令牌生成速度:当令牌被取空后,会被限流。

请求速度=令牌生成速度:流量处于平稳状态。

请求速度<令牌生成速度:请求可被正常处理,桶满则丢弃令牌。

 优点

1、和漏桶算法其实是一样的,不过优化了漏桶出水速度恒定的问题。可以适应突发流量。

缺点

1、系统刚上线就出现大量流量,那么可能会出现由于此时桶中还没有令牌,而导致请求被误杀的情况。(这种情况可以通过上线预热避免

redis集成前准备 

现在我们企业级应用一般用的比较多的是redis的哨兵模式(sentinel)和集群模式(cluster),我们此次专栏就以哨兵模式(sentinel)为例来实现这几种限流策略。

安装redis哨兵模式 

可参考之前的文章阿里云ESC服务器docker安装redis哨兵集群-CSDN博客

SpringBoot3 集成redis

一、maven依赖 

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

 二、application.yml配置 
spring:
  data:
    redis:
      sentinel:
        master: redis_master
        nodes: 127.0.0.1:26379,127.0.0.1:26380,127.0.0.1:26381
      password: Java1104
      lettuce:
        pool:
          max-active: 20
          max-idle: 8
          min-idle: 2
三、redisTemplate配置
package com.limit.rule.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * @author shilei
 */
@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        GenericJackson2JsonRedisSerializer jsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        template.setValueSerializer(jsonRedisSerializer);
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(jsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }

}

后续代码实现会持续更新,敬请期待!!! 

  • 26
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值