自己动手写RateLimiter

本文介绍了限流的基本概念,包括限制速率和限制数量,并探讨了两种常见的限流方案——漏桶和令牌桶。重点讨论了基于令牌桶的限流实现,详细讲解了Guava RateLimiter的工作原理,并逐步展示了如何自己动手实现一个基于令牌桶思想的限流器。通过示例展示了限流器的使用,确保请求以预期的速率进行,防止系统受到过大流量的影响。
摘要由CSDN通过智能技术生成

1. 背景-什么是限流?

限流,顾名思义,其目的就是要限制流量,将其控制在一个合理的范围内,以免大流量对网络或服务器造成压力。

为了达到限流的目的,可以从两个方向着手:

  1. 限制【速率】

    通过控制请求资源的速率,来达到限流的目的。如限制每个访问资源的请求间的时间间隔为200ms。

  2. 限制【数量】

    通过限制允许落到资源上的请求的个数来达到限流的目的。比如我们限制1秒内只允许该资源被请求5次,则QPS=5.

这两种方式的区别或联系如下:

  1. 限制速率,通常是将请求达到资源的速度变为匀速,或者说,每个到达资源的请求之间的时间间隔是固定的。如限制每个访问资源的请求间的时间间隔为200ms。
  2. 限制请求数量,则只关心指定时间内可以访问资源的请求数量是多少,而不限制请求的速率。如1秒内限制某资源只能被访问5次,则在这一秒内,可能在前100毫秒里就来了5个请求,而后面的900毫秒内的请求就会被拒绝。
  3. 限制了请求速率,也就间接的限制了一段时间内的请求量。如限制每个请求的间隔为200ms,则1秒内的请求量就是 1s / 200ms = 5个。

2. 限流的方案

常见的限流方案为漏桶(Leaky Bucket)和令牌桶(Token Bucket)。

3. 限流的实现

基于令牌桶,参考Guava RateLimiter的SmoothBursty实现。

3.1 限流器的使用

Guava RateLimiter是基于令牌桶思想实现的限流器,我们看看如何使用。

使用一个死循环打印一句话。在没有限流的时候,控制台会疯狂打印。

public class RateLimiterDemo {
   
    public static void main(String[] args) {
   
        while (true) {
   
            System.out.println("hello world" + System.currentTimeMillis());
        }
    }
}

此时我们希望限制1秒打印一次,借助RateLimiter可以方便的实现。

public class RateLimiterDemo {
   
    public static void main(String[] args) {
   
        // 调用静态工厂方法,获得RateLimiter实例。传入的参数是期望的QPS
        // 我们希望1秒打印一次,即QPS=1
        RateLimiter rateLimiter = RateLimiter.create(1.0);
        while (true) {
   
            // 申请获取令牌
          	// 若获取成功,则请求通过向下执行;若失败,则当前线程阻塞等待。
            rateLimiter.acquire();
            System.out.println("hello world -- " + System.currentTimeMillis());
        }
    }
}

输出结果:

hello world -- 1590378303960
hello world -- 1590378304960
hello world -- 1590378305963

可见打印已经是1秒一次,限流成功。

3.2 基于令牌桶思想实现限流器

刚才我们使用Guava RateLimiter实现了限流的需求,现在我们将仿照其实现,自己写一个限流器出来,为了方便对应Guava的代码学习,我们将变量、方法都直接沿用Guava RateLimiter的命名。

在这之前,先考虑几个问题,这些问题都会在后续实现的过程中逐一解答:

  1. 令牌桶如何用代码表示?
  2. 令牌的生成如何实现?
  3. 令牌桶的一个特点是支持突发流量,如何实现?

在这里插入图片描述

新建TokenBucketRateLimiter类。

public class TokenBucketRateLimiter {
   
}

我们首先先将目光转移到上图的令牌桶的位置,从图中可知令牌桶应具备的几个属性:

  1. 生成令牌的速率,即每过多长时间生成一个新令牌。
  2. 令牌桶的容量,即最多存储多少块令牌。
  3. 令牌桶中当前剩余的令牌数量。

除了令牌桶自身的三个属性外,我们还需要一个计时器和一个表示“无需等待即可获得令牌的时间”的一个时间戳。

除此之外,令牌桶需要支持突发流量,我们可以指定允许突发流量持续的时间。

所以我们一共需要6个属性,就可以表示出一个令牌桶。

		/**
     * 令牌桶的容量,即最多存储多少块令牌
     */
    private double maxPermits;

    /**
     * 令牌桶中当前剩余的令牌数
     */
    private double storedPermits;

    /**
     * 每隔多长时间生成一块令牌。单位:微秒
     * coolDown表示冷却时间
     * (游戏中技能释放后需要等待一段时间后才能重新释放,这段时间被成为冷却时间)
     */
    private double coolDown;

		/**
     * 无需等待即可获得令牌的时间戳,单位微秒
     */
    private long nextFreePermit
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值