Guava RateLimiter限流器使用示例

Guava中的RateLimiter可以限制单进程中某个方法的速率,本文主要介绍如何使用,实现原理请参考文档:推荐:超详细的Guava RateLimiter限流原理解析推荐:RateLimiter 源码分析(Guava 和 Sentinel 实现)

1 基于spring-mvc的controller测试限流

完整代码可参考:https://github.com/sxpujs/spring-cloud-examples/tree/master/rest-service

1.1 增加Maven依赖:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>29.0-jre</version>
</dependency>

1.2 AccessLimitService 限流Service类

@Service
public class AccessLimitService {

    // 每秒发出5个令牌
    RateLimiter rateLimiter = RateLimiter.create(5.0);

    /**
     * 尝试获取令牌
     */
    public boolean tryAcquire() {
        return rateLimiter.tryAcquire();
    }
}

1.3 控制器类

@RestController
@Slf4j
public class HelloController {

    @Autowired
    private AccessLimitService accessLimitService;

    @RequestMapping("/access")
    public String access() {
        if (accessLimitService.tryAcquire()) {
            log.info("start");
            // 模拟业务执行500毫秒
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "access success [" + LocalDateTime.now() + "]";
        } else {
            //log.warn("限流");
            return "access limit [" + LocalDateTime.now() + "]";
        }
    }
}

1.4 使用wrk工具模拟客户端发起多个请求

我们使用HTTP基准工具wrk来生成大量HTTP请求。在终端输入如下命令来测试:

wrk -t1 -c10 -d2s http://127.0.0.1:8080/access

服务端日志如下所示(稍做简化),可以看出前6行的执行时间是一样的,这是因为RateLimiter的默认实现SmoothBursty会缓存1秒的许可,在定义RateLimiter实例时,每秒5个许可,加上新占用的1个许可,一共有6个。从第7行开始,每0.2秒执行1次,符合预期。

2020-07-05 15:46:16.605  INFO --- [nio-8080-exec-2] HelloController : start
2020-07-05 15:46:16.605  INFO --- [nio-8080-exec-3] HelloController : start
2020-07-05 15:46:16.605  INFO --- [nio-8080-exec-7] HelloController : start
2020-07-05 15:46:16.605  INFO --- [nio-8080-exec-8] HelloController : start
2020-07-05 15:46:16.605  INFO --- [nio-8080-exec-9] HelloController : start
2020-07-05 15:46:16.605  INFO --- [nio-8080-exec-4] HelloController : start
2020-07-05 15:46:16.804  INFO --- [nio-8080-exec-1] HelloController : start
2020-07-05 15:46:17.005  INFO --- [io-8080-exec-11] HelloController : start
2020-07-05 15:46:17.204  INFO --- [nio-8080-exec-9] HelloController : start
2020-07-05 15:46:17.404  INFO --- [nio-8080-exec-5] HelloController : start
2020-07-05 15:46:17.604  INFO --- [nio-8080-exec-8] HelloController : start
2020-07-05 15:46:17.804  INFO --- [nio-8080-exec-2] HelloController : start
2020-07-05 15:46:18.004  INFO --- [nio-8080-exec-7] HelloController : start
2020-07-05 15:46:18.204  INFO --- [nio-8080-exec-6] HelloController : start
2020-07-05 15:46:18.404  INFO --- [nio-8080-exec-5] HelloController : start

2 基于单个类的main方法测试限流

package com.demo.guava;

import com.google.common.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;

@Slf4j
public class RateLimiterDemo {

    static void submitTasks1() {
        ExecutorService pool = Executors.newFixedThreadPool(10);
        RateLimiter rateLimiter = RateLimiter.create(5); // rate is "5 permits per second"
        IntStream.range(0, 10).forEach(i -> pool.submit(() -> {
            if (rateLimiter.tryAcquire()) {
                try {
                    log.info("start");
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                }
            } else {
                log.warn("限流");
            }
        }));
        pool.shutdown();
        /*
16:18:18.784 [pool-1-thread-1] INFO  RateLimiterDemo - start
16:18:18.784 [pool-1-thread-7] WARN  RateLimiterDemo - 限流
16:18:18.784 [pool-1-thread-2] WARN  RateLimiterDemo - 限流
16:18:18.784 [pool-1-thread-4] WARN  RateLimiterDemo - 限流
16:18:18.784 [pool-1-thread-5] WARN  RateLimiterDemo - 限流
16:18:18.784 [pool-1-thread-6] WARN  RateLimiterDemo - 限流
16:18:18.784 [pool-1-thread-9] WARN  RateLimiterDemo - 限流
16:18:18.784 [pool-1-thread-3] WARN  RateLimiterDemo - 限流
16:18:18.784 [pool-1-thread-10] WARN  RateLimiterDemo - 限流
16:18:18.784 [pool-1-thread-8] WARN  RateLimiterDemo - 限流
         */
    }

    static void submitTasks2() {
        ExecutorService pool = Executors.newFixedThreadPool(10);
        RateLimiter rateLimiter = RateLimiter.create(5); // rate is "5 permits per second"
        IntStream.range(0, 10).forEach(i -> pool.submit(() -> {
            rateLimiter.acquire();
            log.info("start");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }));
        pool.shutdown();
        /*
16:18:56.030 [pool-1-thread-1] INFO  RateLimiterDemo - start
16:18:56.227 [pool-1-thread-10] INFO  RateLimiterDemo - start
16:18:56.428 [pool-1-thread-9] INFO  RateLimiterDemo - start
16:18:56.627 [pool-1-thread-8] INFO  RateLimiterDemo - start
16:18:56.827 [pool-1-thread-7] INFO  RateLimiterDemo - start
16:18:57.028 [pool-1-thread-6] INFO  RateLimiterDemo - start
16:18:57.226 [pool-1-thread-5] INFO  RateLimiterDemo - start
16:18:57.426 [pool-1-thread-4] INFO  RateLimiterDemo - start
16:18:57.629 [pool-1-thread-3] INFO  RateLimiterDemo - start
16:18:57.826 [pool-1-thread-2] INFO  RateLimiterDemo - start
         */
    }

    static void submitTasks3() {
        RateLimiter r = RateLimiter.create(5);
        log.info("start");
        for (;;) {
            log.info("get 1 tokens: " + r.acquire() + "s");
        }
        /*
16:15:46.310 [main] INFO RateLimiterDemo - start
16:15:46.315 [main] INFO RateLimiterDemo - get 1 tokens: 0.0s
16:15:46.513 [main] INFO RateLimiterDemo - get 1 tokens: 0.193752s
16:15:46.709 [main] INFO RateLimiterDemo - get 1 tokens: 0.194875s
16:15:46.911 [main] INFO RateLimiterDemo - get 1 tokens: 0.199033s
16:15:47.113 [main] INFO RateLimiterDemo - get 1 tokens: 0.197833s
16:15:47.312 [main] INFO RateLimiterDemo - get 1 tokens: 0.195898s
         */
    }

    static void submitTasks4() {
        RateLimiter r = RateLimiter.create(5);
        log.info("start");
        for (;;) {
            if (r.tryAcquire()) {
                log.info("run");
            }
        }
        /*
16:17:17.098 [main] INFO  RateLimiterDemo - start
16:17:17.100 [main] INFO  RateLimiterDemo - run
16:17:17.296 [main] INFO  RateLimiterDemo - run
16:17:17.496 [main] INFO  RateLimiterDemo - run
16:17:17.696 [main] INFO  RateLimiterDemo - run
         */
    }

    public static void main(String[] args) throws InterruptedException {
        //submitTasks1();
        submitTasks2();
        //submitTasks3();
        //submitTasks4();
    }
}

参考文档:

posted on 2020-07-05 16:30  大鹏123  阅读(4060)  评论(0编辑  收藏  举报