RateLimiter
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>22.0</version>
</dependency>
这个同名的类在nacos的jar包中也有出现。速率限制于java.util.concurrent.Semaphore功能相反,后者限制并发的访问数量,而不是速率(并发和速率密切相关)。见下面的Little定律
RateLimiter主要由颁发许可证的速率来定义。如果没有额外的配置,许可证将以固定的速率分配,以每秒的许可证定义。许可证将顺利分配,并调整各个许可证之间的延迟,以确保维持配置的费率。可以将RateLimiter配置为有一个预热期,在此期间,每秒发出的许可稳步增加,直到达到稳定的速率。
例如:任务每秒执行不超过2次
final RateLimiter rateLimiter = RateLimiter.create(2.0); // 速率为“每秒2次许可”
void submitTasks(List<Runnable> tasks, Executor executor) {
for (Runnable task : tasks) {
rateLimiter.acquire(); // 等待
executor.execute(task);
}
}
例如:发送一个数据流,控制其速度在每秒为5kb,可以通过每个自己一个许可实现,并指定每秒5000个许可的速率
final RateLimiter rateLimiter = RateLimiter.create(5000.0); // rate = 5000 permits per second
void submitPacket(byte[] packet) {
rateLimiter.acquire(packet.length);
networkService.send(packet);
}
for循环每秒打印一行,RateLimiter.create(1000);为什么会这样:
应为速率是指定为1000次/秒(类似频率),这个时候你一次要获取1000个凭证,因为这个会阻塞当前获取,所以你要算什么时候能获取到所有凭证的时间为:所需凭证数(1000)÷速率(1000次/秒) = 1秒
public class TestRateLimiter {
private static final Logger log = LoggerFactory.getLogger(PostExecutor.class);
public static void main(String[] args) {
// 每秒最多允许1000个操作(每秒只能发送1000个凭证)
RateLimiter limiter = RateLimiter.create(1000);
log.info("start");
for (int i = 0; i < 10; i++) {
// 获取1000个凭证,获取不到则等待
limiter.acquire(1000);
log.info("I: " + i);
}
}
}
在一定时间内获取对应的凭证 ,超时获取不到结束
private static void tryAcquireTest1(RateLimiter limiter, int i){
log.info("result: " + limiter.tryAcquire(20, 20000, TimeUnit.MILLISECONDS) + ", " + i);
}
这里的速率是2次每秒,可是我阻塞事件获取20个凭证,意味着10s执行一次,在多线程竞争的情况下,有一个线程多次false结束而且时间也没有达到20s(猜测是不是因为其他两个线程一共获取了40个凭证,意味着这20s之内不会再有新凭证了,所以他就不会非要等到20s才结束获取凭证,就提前结束了),启动两个线程,发现就会交替输出了,但是也出现了多次false的情况
public class TestRateLimiter {
private static final Logger log = LoggerFactory.getLogger(PostExecutor.class);
public static void main(String[] args) {
RateLimiter limiter = RateLimiter.create(2);
log.info("start");
Thread t1 = new Thread(()->{tryAcquireTest(limiter);});
Thread t2 = new Thread(()->{tryAcquireTest(limiter);});
Thread t3 = new Thread(()->{tryAcquireTest(limiter);});
t1.start();
t2.start();
t3.start();
}
private static void tryAcquireTest(RateLimiter limiter){
for (int i = 0; i < 10; i++) {
tryAcquireTest1(limiter,i);
}
}
private static void tryAcquireTest1(RateLimiter limiter, int i){
log.info(Thread.currentThread().getName() + "result: " + limiter.tryAcquire(20, 20000, TimeUnit.MILLISECONDS) + ", " + i);
}
}