Failsafe
Installation (Gradle)¶
dependencies {
implementation(platform("org.http4k:http4k-bom:5.10.4.0"))
implementation("org.http4k:http4k-failsafe")
}
About¶
This module provides a configurable Filter to provide fault tolerance (CircuitBreaking, RateLimiting, Retrying, Bulkheading, Timeouts etc.), by integrating with the Failsafe library.
Basic example
¶
Here's an example that uses BulkHeading to demonstrate how easy it is to use the filter with configured Failsafe policies.
package guide.reference.failsafe
import dev.failsafe.Bulkhead
import dev.failsafe.Failsafe
import org.http4k.core.Method.GET
import org.http4k.core.Request
import org.http4k.core.Response
import org.http4k.core.Status.Companion.OK
import org.http4k.core.then
import org.http4k.filter.FailsafeFilter
import kotlin.concurrent.thread
fun main() {
// Configure a Failsafe policy
val failsafeExecutor = Failsafe.with(
Bulkhead.of<Response>(5)
)
// Use the filter in a filter chain
val app = FailsafeFilter(failsafeExecutor).then {
Thread.sleep(100)
Response(OK)
}
// Throw a bunch of requests at the filter - only 5 should pass
for (it in 1..10) {
thread {
println(app(Request(GET, "/")).status)
}
}
}
Example of using multiple policies
¶
Using multiple Failsafe policies in the filter is just as easy, as the following example shows.
package guide.reference.failsafe
import dev.failsafe.CircuitBreaker
import dev.failsafe.Failsafe
import dev.failsafe.Fallback
import dev.failsafe.RetryPolicy
import dev.failsafe.Timeout
import org.http4k.core.Method.GET
import org.http4k.core.Request
import org.http4k.core.Response
import org.http4k.core.Status.Companion.BAD_REQUEST
import org.http4k.core.Status.Companion.INTERNAL_SERVER_ERROR
import org.http4k.core.Status.Companion.OK
import org.http4k.core.then
import org.http4k.filter.FailsafeFilter
import java.time.Duration
fun main() {
// Configure multiple Failsafe policies
val failsafeExecutor = Failsafe.with(
Fallback.of(Response(OK).body("Fallback")),
RetryPolicy.builder<Response>()
.withMaxAttempts(2)
.handleResultIf { !it.status.successful }
.build(),
CircuitBreaker.builder<Response>()
.withFailureThreshold(1)
.handleResultIf { it.status.serverError }
.build(),
Timeout.of(Duration.ofMillis(100))
)
// We then create a very unstable client using the filter
var requestCount = 0
val client = FailsafeFilter(failsafeExecutor).then {
requestCount++
when (requestCount % 4) {
1, 3 -> Response(OK).body("All good - $requestCount")
0 -> Response(INTERNAL_SERVER_ERROR)
else -> Response(BAD_REQUEST)
}
}
// Throw a bunch of request at the filter - some will fail and be retried until
// the circuit breaker opens and the fallback value will be used after that.
for (it in 1..5) {
val response = client(Request(GET, "/"))
println("${response.status} - ${response.bodyString()}")
}
}