RateLimiter 从概念上来讲,速率限制器会在可配置的速率下分配许可证。如果必要的话,每个acquire() 会阻塞当前线程直到许可证可用后获取该许可证。一旦获取到许可证,不需要再释放许可证。
校对注:RateLimiter使用的是一种叫令牌桶的流控算法,RateLimiter会按照一定的频率往桶里扔令牌,线程拿到令牌才能执行,比如你希望自己的应用程序QPS不要超过1000,那么RateLimiter设置1000的速率后,就会每秒往桶里扔1000个令牌。
1com.google.common.util.concurrent.RateLimiter 2 3@ThreadSafe 4@Betapublic 5 abstract class RateLimiter extends ObjectRateLimiter经常用于限制对一些物理资源或者逻辑资源的访问速率。与Semaphore 相比,Semaphore 限制了并发访问的数量而不是使用速率。(注意尽管并发性和速率是紧密相关的,比如参考Little定律)
通过设置许可证的速率来定义RateLimiter。在默认配置下,许可证会在固定的速率下被分配,速率单位是每秒多少个许可证。为了确保维护配置的速率,许可会被平稳地分配,许可之间的延迟会做调整。 可能存在配置一个拥有预热期的RateLimiter 的情况,在这段时间内,每秒分配的许可数会稳定地增长直到达到稳定的速率。
举例来说明如何使用RateLimiter,想象下我们需要处理一个任务列表,但我们不希望每秒的任务提交超过两个:
1//速率是每秒两个许可 2 final RateLimiter rateLimiter = RateLimiter.create(2.0); 3 4 void submitTasks(List tasks, Executor executor) { 5 for (Runnable task : tasks) { 6 rateLimiter.acquire(); // 也许需要等待 7 executor.execute(task); 8 } 9}再举另外一个例子,想象下我们制造了一个数据流,并希望以每秒5kb的速率处理它。可以通过要求每个字节代表一个许可,然后指定每秒5000个许可来完成:
1// 每秒5000个许可 2 final RateLimiter rateLimiter = RateLimiter.create(5000.0); 3 4 void submitPacket(byte[] packet) { 5 rateLimiter.acquire(packet.length); 6 networkService.send(packet); 7}有一点很重要,那就是请求的许可数从来不会影响到请求本身的限制(调用acquire(1) 和调用acquire(1000) 将得到相同的限制效果,如果存在这样的调用的话),但会影响下一次请求的限制,也就是说,如果一个高开销的任务抵达一个空闲的RateLimiter,它会被马上许可,但是下一个请求会经历额外的限制,从而来偿付高开销任务。注意:RateLimiter 并不提供公平性的保证。
Since:13.0 作者: Dimitris Andreou
抛出:
IllegalArgumentException – 如果permitsPerSecond为负数或者为0 setRatepublic final void setRate(double permitsPerSecond) 更新RateLimite的稳定速率,参数permitsPerSecond 由构造RateLimiter的工厂方法提供。调用该方法后,当前限制线程不会被唤醒,因此他们不会注意到最新的速率;只有接下来的请求才会。需要注意的是,由于每次请求偿还了(通过等待,如果需要的话)上一次请求的开销,这意味着紧紧跟着的下一个请求不会被最新的速率影响到,在调用了setRate 之后;它会偿还上一次请求的开销,这个开销依赖于之前的速率。RateLimiter的行为在任何方式下都不会被改变,比如如果 RateLimiter 有20秒的预热期配置,在此方法被调用后它还是会进行20秒的预热。参数: permitsPerSecond – RateLimiter的新的稳定速率抛出: IllegalArgumentException – 如果permitsPerSecond为负数或者为0 getRatepublic final double getRate() 返回RateLimiter 配置中的稳定速率,该速率单位是每秒多少许可数。它的初始值相当于构造这个RateLimiter的工厂方法中的参数permitsPerSecond ,并且只有在调用setRate(double)后才会被更新。 acquirepublic double acquire() 从RateLimiter获取一个许可,该方法会被阻塞直到获取到请求。如果存在等待的情况的话,告诉调用者获取到该请求所需要的睡眠时间。该方法等同于acquire(1)。返回: time spent sleeping to enforce rate, in seconds; 0.0 if not rate-limited 执行速率的所需要的睡眠时间,单位为妙;如果没有则返回0Since: 16.0 (版本13.0没有返回值) acquirepublic double acquire(int permits) 从RateLimiter获取指定许可数,该方法会被阻塞直到获取到请求数。如果存在等待的情况的话,告诉调用者获取到这些请求数所需要的睡眠时间。参数: permits – 需要获取的许可数返回: 执行速率的所需要的睡眠时间,单位为妙;如果没有则返回0抛出: IllegalArgumentException – 如果请求的许可数为负数或者为0Since: 16.0 (版本13.0没有返回值) tryAcquirepublic boolean tryAcquire(long timeout,TimeUnit unit) 从RateLimiter获取许可如果该许可可以在不超过timeout的时间内获取得到的话,或者如果无法在timeout 过期之前获取得到许可的话,那么立即返回false(无需等待)。该方法等同于tryAcquire(1, timeout, unit)。参数: timeout – 等待许可的最大时间,负数以0处理unit – 参数timeout 的时间单位返回: true表示获取到许可,反之则是false抛出: IllegalArgumentException – 如果请求的许可数为负数或者为0
tryAcquirepublic boolean tryAcquire(int permits) 从RateLimiter 获取许可数,如果该许可数可以在无延迟下的情况下立即获取得到的话。该方法等同于tryAcquire(permits, 0, anyUnit)。参数: permits – 需要获取的许可数返回: true表示获取到许可,反之则是false抛出: IllegalArgumentException – 如果请求的许可数为负数或者为0Since: 14.0 tryAcquirepublic boolean tryAcquire() 从RateLimiter 获取许可,如果该许可可以在无延迟下的情况下立即获取得到的话。 该方法等同于tryAcquire(1)。返回: true表示获取到许可,反之则是falseSince: 14.0 tryAcquirepublic boolean tryAcquire(int permits,long timeout,TimeUnit unit) 从RateLimiter 获取指定许可数如果该许可数可以在不超过timeout的时间内获取得到的话,或者如果无法在timeout 过期之前获取得到许可数的话,那么立即返回false (无需等待)。参数: permits – 需要获取的许可数 timeout – 等待许可数的最大时间,负数以0处理 unit – 参数timeout 的时间单位返回: true表示获取到许可,反之则是false抛出: IllegalArgumentException -如果请求的许可数为负数或者为0 toStringpublic String toString() 以下描述复制于java.lang.Object类。 返回对象的字符表现形式。通常来讲,toString 方法返回一个“文本化呈现”对象的字符串。 结果应该是一个简明但易于读懂的信息表达式。建议所有子类都重写该方法。 toString 方法返回一个由实例的类名,字符’@’和以无符号十六进制表示的对象的哈希值组成的字符串。换句话说,该方法返回的字符串等同于: getClass().getName() + ‘@’ + Integer.toHexString(hashCode())重载: Object类的toString方法返回: 对象的字符表现形式 转载自 并发编程网 - ifeve.com 相关资源:敏捷开发V1.0.pptx