A Kotlin wrapper around Bucket4j which suspends and plays nicely with coroutines.
See the changelog.
- Not another token bucket implementation: Uses Bucket4k's tried and true token bucket implementation.
- No locks: Wraps Bucket4j's LockFreeBucket.
- Suspendable functions: None of the included functionality blocks the calling thread, instead suspending coroutine execution while waiting for available tokens. No locks means no blocked threads waiting for said locks.
Add Bucket4k as a project dependency:
build.gradle.kts:
dependencies {
implementation("com.sletmoe.bucket4k:bucket4k:<version>")
}
Create a SuspendingBucket, configuring token limits as desired:
val tokenBucket = SuspendingBucket.build {
addLimit(Bandwidth.simple(5, 1.seconds.toJavaDuration()))
addLimit(Bandwidth.simple(30, 1.minutes.toJavaDuration()))
}
We can then use the token bucket. For example, to limit transactions per second on an API operation within a Ktor web application:
fun Route.widgetRouting() {
route("/widget") {
get {
if (tokenBucket.tryConsume(1)) {
call.respond(listWidgets())
} else {
call.respondText("Too many requests! Slow your roll.", status = HttpStatusCode.TooManyRequests)
}
}
}
}
Or we could use it to rate-limit asynchronous calls to some API:
val webPagesToGet = listOf("https://my.web.page/foo", ...)
val results = webPagesToGet.map { webPageUri ->
async(Dispatchers.IO) {
tokenBucket.consume(1)
httpClient.get(webPageUri)
}
}.map { it.await() }
Read the API documentation for more information.