Guava中的RateLimiter能夠限制單進程中某個方法的速率,本文主要介紹如何使用,實現原理請參考文檔:推薦:超詳細的Guava RateLimiter限流原理解析和推薦:RateLimiter 源碼分析(Guava 和 Sentinel 實現)。html
完整代碼可參考:https://github.com/sxpujs/spring-cloud-examples/tree/master/rest-servicejava
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>29.0-jre</version> </dependency>
@Service public class AccessLimitService { // 每秒發出5個令牌 RateLimiter rateLimiter = RateLimiter.create(5.0); /** * 嘗試獲取令牌 */ public boolean tryAcquire() { return rateLimiter.tryAcquire(); } }
@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() + "]"; } } }
咱們使用HTTP基準工具wrk來生成大量HTTP請求。在終端輸入以下命令來測試:git
wrk -t1 -c10 -d2s http://127.0.0.1:8080/access
服務端日誌以下所示(稍作簡化),能夠看出前6行的執行時間是同樣的,這是由於RateLimiter的默認實現SmoothBursty會緩存1秒的許可,在定義RateLimiter實例時,每秒5個許可,加上新佔用的1個許可,一共有6個。從第7行開始,每0.2秒執行1次,符合預期。github
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
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(); } }
參考文檔:spring