RateLimiter 是基于令牌桶实现的限速。
引入依赖
如果是使用 gradle 管理 Java 依赖,在 dependencies 中配置:
1
| compile "com.google.guava:guava:$latestVersion"
|
示例1:匀速执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| import com.google.common.util.concurrent.RateLimiter;
import java.time.LocalTime;
public class RateLimiterTest {
public static void main(String[] args) { RateLimiter rateLimiter = RateLimiter.create(5); for (int i=0; i< 10; i++) { rateLimiter.acquire(); System.out.println(LocalTime.now()); } }
}
|
运行结果:
1 2 3 4 5 6 7 8 9 10
| 10:23:53.612 10:23:53.741 10:23:53.940 10:23:54.137 10:23:54.335 10:23:54.538 10:23:54.737 10:23:54.935 10:23:55.135 10:23:55.339
|
可以看到,基本是相隔200ms打印一次时间。
这里第一次和第二次之间相隔的是 130ms ,但是下面的示例中又说明的确过了 200ms 左右。为什么 ?
待分析。
acquire 方法会返回秒级的耗时:
1 2 3 4 5 6 7 8 9 10 11 12 13
| import com.google.common.util.concurrent.RateLimiter;
public class RateLimiterTest {
public static void main(String[] args) { RateLimiter rateLimiter = RateLimiter.create(5); for (int i=0; i< 10; i++) { double timeCost = rateLimiter.acquire(); System.out.println("获取令牌耗时(单位秒): " + timeCost); } }
}
|
运行结果示例:
1 2 3 4 5 6 7 8 9 10
| 获取令牌耗时(单位秒): 0.0 获取令牌耗时(单位秒): 0.198251 获取令牌耗时(单位秒): 0.197075 获取令牌耗时(单位秒): 0.195677 获取令牌耗时(单位秒): 0.19635 获取令牌耗时(单位秒): 0.196841 获取令牌耗时(单位秒): 0.195845 获取令牌耗时(单位秒): 0.194625 获取令牌耗时(单位秒): 0.199044 获取令牌耗时(单位秒): 0.1966
|
示例2:突发流量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import com.google.common.util.concurrent.RateLimiter; import com.google.common.util.concurrent.Uninterruptibles;
import java.util.concurrent.TimeUnit;
public class RateLimiterTest {
public static void main(String[] args) { RateLimiter rateLimiter = RateLimiter.create(5); System.out.println("sleep 5秒"); Uninterruptibles.sleepUninterruptibly(5, TimeUnit.SECONDS); for (int i=0; i< 10; i++) { double timeCost = rateLimiter.acquire(); System.out.println("获取令牌耗时(单位秒): " + timeCost); } }
}
|
运行结果:
1 2 3 4 5 6 7 8 9 10
| 获取令牌耗时(单位秒): 0.0 获取令牌耗时(单位秒): 0.0 获取令牌耗时(单位秒): 0.0 获取令牌耗时(单位秒): 0.0 获取令牌耗时(单位秒): 0.0 获取令牌耗时(单位秒): 0.0 获取令牌耗时(单位秒): 0.196621 获取令牌耗时(单位秒): 0.197801 获取令牌耗时(单位秒): 0.199491 获取令牌耗时(单位秒): 0.200235
|
RateLimiter.create(5) 指定每秒匀速产生 5 个令牌,且令牌桶中最多5个令牌。
示例代码中获取的前5个令牌是之前颁发的已经在令牌桶中的令牌,所以获取的很快,几乎是同一时间旧全部拿出了。
示例3:预热限速
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| import com.google.common.util.concurrent.RateLimiter;
import java.util.concurrent.TimeUnit;
public class RateLimiterTest {
public static void main(String[] args) {
RateLimiter rateLimiter = RateLimiter.create(2, 3, TimeUnit.SECONDS); for (int i=0; i< 10; i++) { double timeCost = rateLimiter.acquire(); System.out.println("获取令牌耗时(单位秒): " + timeCost); } }
}
|
执行结果示例:
1 2 3 4 5 6 7 8 9 10
| 获取令牌耗时(单位秒): 0.0 获取令牌耗时(单位秒): 1.331039 获取令牌耗时(单位秒): 0.996633 获取令牌耗时(单位秒): 0.664236 获取令牌耗时(单位秒): 0.496114 获取令牌耗时(单位秒): 0.496574 获取令牌耗时(单位秒): 0.498551 获取令牌耗时(单位秒): 0.49505 获取令牌耗时(单位秒): 0.498739 获取令牌耗时(单位秒): 0.499335
|
可以看到,耗时是逐步减少到500ms左右。