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);}
}