当前位置:   article > 正文

精尽 Redisson 源码分析 —— 限流器 RateLimiter_redissonratelimiter

redissonratelimiter

1. 概述

限流,无论在系统层面,还是在业务层面,使用都非常广泛。例如说:

  • 【业务】为了避免恶意的灌水机或者用户,限制每分钟至允许回复 10 个帖子。
  • 【系统】为了避免服务系统被大规模调用,超过极限,限制每个调用方只允许每秒调用 100 次。

限流算法,常用的分成四种:

每一种的概念,推荐看看 《计数器、滑动窗口、漏桶、令牌算法比较和伪代码实现》 文章。

  • 计数器

    比较简单,每固定单位一个计数器即可实现。

  • 滑动窗口

    Redisson 提供的是基于滑动窗口 RateLimiter 的实现。相比计数器的实现,它的起点不是固定的,而是以开始计数的那个时刻开始为一个窗口。

    所以,我们可以把计数器理解成一个滑动窗口的特例,以固定单位为一个窗口。

  • 令牌桶算法

    《Eureka 源码解析 —— 基于令牌桶算法的 RateLimiter》 ,单机并发场景下的 RateLimiter 实现。

    《Spring-Cloud-Gateway 源码解析 —— 过滤器 (4.10) 之 RequestRateLimiterGatewayFilterFactory 请求限流》 ,基于 Redis 实现的令牌桶算法的 RateLimiter 实现。

  • 漏桶算法

    漏桶算法,一直没搞明白和令牌桶算法的区别。现在的理解是:

    • 令牌桶算法,桶里装的是令牌。每次能拿取到令牌,就可以进行访问。并且,令牌会按照速率不断恢复放到令牌桶中直到桶满。
    • 漏桶算法,桶里装的是请求。当桶满了,请求就进不来。例如说,Hystrix 使用线程池或者 Semaphore 信号量,只有在请求未满的时候,才可以进行执行。

上面哔哔了非常多的字,只看本文的话,就那一句话:“Redisson 提供的是基于滑动窗口 RateLimiter 的实现。”。

2. 整体一览

在 Redisson 中,提供了四个 RateLimiter 相关的接口,如下图:

正在上传…重新上传取消RateLimiter 接口

目前,Redisson 暂时只实现了 RRateLimiterAsync 和 RRateLimiter 接口的方法,即 org.redisson.RedissonRateLimiter 。

RRateLimiterAsync 和 RRateLimiter 定义的接口,差别就在于同步和异步,所以我们就只看看 RRateLimiter 接口。代码如下:

boolean trySetRate(RateType mode, long rate, long rateInterval, RateIntervalUnit rateIntervalUnit);
RateLimiterConfig getConfig();

boolean tryAcquire();
boolean tryAcquire(long permits);
boolean tryAcquire(long timeout, TimeUnit unit);
boolean tryAcquire(long permits, long timeout, TimeUnit unit);

void acquire();
void acquire(long permits);
  • #trySetRate(RateType mode, long rate, long rateInterval, RateIntervalUnit rateIntervalUnit) 方法,设置限流器的配置。
  • #getConfig() 方法,获得限流器的配置。
  • #tryAcquire(...) 方法,尝试在指定时间内,获得指定数量的令牌,并返回是否成功。
  • #acquire(...) 方法,在指定时间内,获得指定数量的令牌,直到成功。

总的来说,一共两类方法,一类是设置或获取配置,一类是获取令牌。下面,我们来逐个方法的源码,来瞅瞅。

3. trySetRate

在 《精尽 Redisson 源码分析 —— 调试环境搭建》 中,我们搭建了一个限流器的示例。在示例的开始,我们会调用 RRateLimiter#trySetRateAsync(RateType type, long rate, long rateInterval, RateIntervalUnit unit) 方法,设置限流器的配置。代码如下:

// RedissonRateLimiter.java

@Override
public boolean trySetRate(RateType type, long rate, long rateInterval, RateIntervalUnit unit) {
    return get(trySetRateAsync(type, rate, rateInterval, unit));
}

@Override
public RFuture<Boolean> trySetRateAsync(RateType type, long rate, long rateInterval, RateIntervalUnit unit) {
    return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
            "redis.call('hsetnx', KEYS[1], 'rate', ARGV[1]);"
          + "redis.call('hsetnx', KEYS[1], 'interval', ARGV[2]);"
          + "return redis.call('hsetnx', KEYS[1], 'type', ARGV[3]);",
            Collections.<Object>singletonList(getName()), // keys [分布锁名]
            rate, unit.toMillis(rateInterval), type.ordinal()); // values [速度、速度单位、限流类型]
}
  • 将限流器的配置写入到 Redis 中。这个和我们看到的很多分布式限流器的实现不同,它们只提供获取令牌的功能,而 Redisson 多了持久化配置限流器的配置到 Redis 中,相当于说,Redis 起到了一个配置中心的功能,分布式下的相同限流器(“相同”指的是相同名字的限流器)使用同一的配置。
  • 参数 type :类型是 org.redisson.api.RateType ,限流类型。目前有两种:
    • OVERALL :相同名字的所有 RateLimiter 实例。
    • PER_CLIENT :相同 JVM 进程的所有相同名字的所有 RateLimiter 实例。
      声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/正经夜光杯/article/detail/883818
推荐阅读
相关标签