因此,假设我正在运行一些与某些Web服务交互的Couroutine,由于我不想向其发送垃圾邮件,因此我希望将请求限制为每x秒最大1个请求。为此,我可以使用以下代码:
fun CoroutineScope.rateLimiter(tokens: SendChannel<Unit>, rate: Int) = launch {
var lastToken = System.currentTimeMillis()
while (isActive) {
val currentTime = System.currentTimeMillis()
if (currentTime - lastToken < rate) {
delay(currentTime - lastToken)
}
tokens.send(Unit)
}
}
fun CoroutineScope.request(tokens: ReceiveChannel<Unit>) = launch {
for (token in tokens) {
//Do Web request
}
}
1。)这样有效吗?
2。)这不能扩展为将某些内容限制为x字节/秒或需要从Token Bucket中请求x令牌的内容,这是实现类似内容的最佳方法用协程?
答案 0 :(得分:0)
如果您不想依赖于可能产生的许可多于消耗的许可的工作和渠道,然后在某个流程开始接受许可后就蜂拥而至,也许这就是适合您的解决方案。
(这里有一些 jvm 风格,但可替换为多平台)
import kotlin.math.max
import kotlinx.coroutines.delay
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
class SimpleRateLimiter(eventsPerSecond: Double) {
private val mutex = Mutex()
@Volatile
private var next: Long = Long.MIN_VALUE
private val delayNanos: Long = (1_000_000_000L / eventsPerSecond).toLong()
/**
* Suspend the current coroutine until it's calculated time of exit
* from the rate limiter
*/
suspend fun acquire() {
val now: Long = System.nanoTime()
val until = mutex.withLock {
max(next, now).also {
next = it + delayNanos
}
}
if (until != now) {
delay((until - now) / 1_000_000)
}
}
}
它还有其他的权衡。
如果你想要一个 IntervalLimiter 允许 X 请求每 Y 秒,然后抛出异常,有 RateLimiter in Resilience4J 或者,如果您想要功能更全面的东西,我正在研究 PR to create both a RateLimiter and an IntervalLimiter in coroutines core project。