<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>27.1-jre</version> </dependency>
AccessLimitService.java 限流服務封裝到一個類中AccessLimitService,提供tryAcquire()方法,用來嘗試獲取令牌,返回true表示獲取到
@Service public class AccessLimitService { //每秒只發出5個令牌 RateLimiter rateLimiter = RateLimiter.create(5.0); /** * 嘗試獲取令牌 * @return */ public boolean tryAcquire(){ return rateLimiter.tryAcquire(); } }
Controller層每次收到請求的時候都嘗試去獲取令牌,獲取成功和失敗打印不同的信息
import com.google.common.util.concurrent.RateLimiter; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * Created by wuwf on 17/7/11. * 有很多個任務,但希望每秒不超過X個,可用此類 */ public class Demo1 { public static void main(String[] args) { //0.5代表一秒最多多少個 RateLimiter rateLimiter = RateLimiter.create(0.5); List<Runnable> tasks = new ArrayList<Runnable>(); for (int i = 0; i < 10; i++) { tasks.add(new UserRequest(i)); } ExecutorService threadPool = Executors.newCachedThreadPool(); for (Runnable runnable : tasks) { System.out.println("等待時間:" + rateLimiter.acquire()); threadPool.execute(runnable); } } private static class UserRequest implements Runnable { private int id; public UserRequest(int id) { this.id = id; } public void run() { System.out.println(id); } } }
我們限制了2秒放行一個,可以看到第一個是直接執行了,後面的每2秒會放行一個。
rateLimiter.acquire()該方法會阻塞線程,直到令牌桶中能取到令牌爲止才繼續向下執行,並返回等待的時間。
RateLimiter方法摘要
- 修飾符和類型 方法和描述
- double acquire() 從RateLimiter獲取一個許可,該方法會被阻塞直到獲取到請求
- double acquire(int permits)從RateLimiter獲取指定許可數,該方法會被阻塞直到獲取到請求
- static RateLimiter create(double permitsPerSecond)根據指定的穩定吞吐率創建RateLimiter,這裏的吞吐率是指每秒多少許可數(通常是指QPS,每秒多少查詢)
- static RateLimiter create(double permitsPerSecond, long warmupPeriod, TimeUnit unit)根據指定的穩定吞吐率和預熱期來創建RateLimiter,這裏的吞吐率是指每秒多少許可數(通常是指QPS,每秒多少個請求量),在這段預熱時間內,
- RateLimiter每秒分配的許可數會平穩地增長直到預熱期結束時達到其最大速率。(只要存在足夠請求數來使其飽和)
- double getRate()返回RateLimiter 配置中的穩定速率,該速率單位是每秒多少許可數
- void setRate(double permitsPerSecond)更新RateLimite的穩定速率,參數permitsPerSecond 由構造RateLimiter的工廠方法提供。 String toString()返回對象的字符表現形式
- boolean tryAcquire()從RateLimiter 獲取許可,如果該許可可以在無延遲下的情況下立即獲取得到的話
- boolean tryAcquire(int permits)從RateLimiter 獲取許可數,如果該許可數可以在無延遲下的情況下立即獲取得到的話 boolean tryAcquire(int permits, long timeout, TimeUnit unit)從RateLimiter 獲取指定許可數如果該許可數可以在不超過timeout的時間內獲取得到的話,或者如果無法在timeout 過期之前獲取得到許可數的話,那麼立即返回false (無需等待)
- boolean tryAcquire(long timeout, TimeUnit unit)從RateLimiter 獲取許可如果該許可可以在不超過timeout的時間內獲取得到的話,或者如果無法在timeout 過期之前獲取得到許可的話,那麼立即返回false(無需等待) 舉例來說明如何使用RateLimiter,想象下我們需要處理一個任務列表,但我們不希望每秒的任務提交超過兩個:
//速率是每秒兩個許可 final RateLimiter rateLimiter = RateLimiter.create(2.0); void submitTasks(List tasks, Executor executor) { for (Runnable task : tasks) { rateLimiter.acquire(); // 也許需要等待 executor.execute(task); } }
官方文檔:http://ifeve.com/guava-ratelimiter
Java程序員羣:956058372,加入領取架構資料一起交流學習!