RateLimiter 是基于令牌桶实现的限速。
引入依赖
如果是使用 gradle 管理 Java 依赖,在 dependencies 中配置:
compile "com.google.guava:guava:$latestVersion" // 建议使用最新版本
示例1:匀速执行
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());
}
}
}
运行结果:
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
方法会返回秒级的耗时:
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);
}
}
}
运行结果示例:
获取令牌耗时(单位秒): 0.0
获取令牌耗时(单位秒): 0.198251
获取令牌耗时(单位秒): 0.197075
获取令牌耗时(单位秒): 0.195677
获取令牌耗时(单位秒): 0.19635
获取令牌耗时(单位秒): 0.196841
获取令牌耗时(单位秒): 0.195845
获取令牌耗时(单位秒): 0.194625
获取令牌耗时(单位秒): 0.199044
获取令牌耗时(单位秒): 0.1966
示例2:突发流量
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);
}
}
}
运行结果:
获取令牌耗时(单位秒): 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:预热限速
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);
}
}
}
执行结果示例:
获取令牌耗时(单位秒): 0.0
获取令牌耗时(单位秒): 1.331039
获取令牌耗时(单位秒): 0.996633
获取令牌耗时(单位秒): 0.664236
获取令牌耗时(单位秒): 0.496114
获取令牌耗时(单位秒): 0.496574
获取令牌耗时(单位秒): 0.498551
获取令牌耗时(单位秒): 0.49505
获取令牌耗时(单位秒): 0.498739
获取令牌耗时(单位秒): 0.499335
可以看到,耗时是逐步减少到500ms左右。