6种限流方法之服务端令牌算法(结合guava工具包)

令牌算法

在令牌桶算法中有一个程序以某种恒定的速度生成令牌,并存入令牌桶中。每个请求必须先获取令牌才能执行,请求如果没有获取到令牌,可以选择等待,也可以放弃执行,如下图所示:

6种限流方法之服务端令牌算法(结合guava工具包)

我们可以使用 Google 开源的 guava 包,很方便的实现令牌桶算法,首先在 pom.xml 添加 guava 引用,配置如下:

1

2

3

4

5

6

<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->

<dependency>

    <groupId>com.google.guava</groupId>

    <artifactId>guava</artifactId>

    <version>28.2-jre</version>

</dependency>

具体实现代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

package com.example.demo;

 

import com.google.common.util.concurrent.RateLimiter;

 

import java.time.Instant;

 

/**

 * 6种限流方法之服务端令牌算法

 *

 * @author www.jiagou1216.com

 */

public class RateLimiterDemo {

    public static void main(String[] args) {

        // 每秒产生 10 个令牌(每 100 ms 产生一个)

        RateLimiter rt = RateLimiter.create(10);

        for (int i = 0; i < 11; i++) {

            new Thread(() -> {

                // 获取 1 个令牌

                rt.acquire();

                System.out.println("正常执行方法,ts:" + Instant.now());

            }).start();

        }

    }

}

以上程序的执行结果为:

6种限流方法之服务端令牌算法(结合guava工具包)

可以看到,令牌确实是每 100ms 产生一个,而 acquire() 方法为阻塞等待获取令牌,它可以接收一个 int 类型的参数,用于指定获取令牌的个数。替代方法还有 tryAcquire(),此方法在没有可用令牌时就会返回 false 这样就不会阻塞等待了。tryAcquire() 方法也可以设置超时时间,未超过最大等待时间会阻塞等待获取令牌,如果超过了最大等待时间仍然没有可用的令牌就会返回 false。

注意:

使用 google guava 实现的令牌算法属于程序级别的单机版限流方案,而上面使用 Redis-Cell 的是分布式的限流方案。

baidu/google/weixin: 架构师小跟班