• 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();
        }
    }
    

    参考文档:

  • 相关阅读:
    test
    莫烦Python
    资源汇总
    AutoHotkey学习资源
    神器AutoHotkey学习(官方文档翻译)
    linux内核编译时bad register name `%dil'错误
    Linux内核修炼之道-->原作者博客链接--oschina备份
    vim笔记--oschina备份
    vim相关资源--oschina备份
    Windows下装Gvim时可能发生的错误--oschina备份
  • 原文地址:https://www.cnblogs.com/sxpujs/p/13246563.html
Copyright © 2020-2023  润新知