forked from ReactiveX/RxJava
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add resilience4j-kotlin module (ReactiveX#441)
- Loading branch information
1 parent
82a1f43
commit c846f9e
Showing
18 changed files
with
1,146 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
77 changes: 77 additions & 0 deletions
77
resilience4j-documentation/src/docs/asciidoc/addon_guides/kotlin.adoc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
=== Kotlin | ||
|
||
==== Introduction | ||
|
||
Integration for https://kotlinlang.org/[Kotlin] coroutines that enables executing and decorating `suspend` functions with the various resilience aspects provided by the core modules. | ||
|
||
Extension functions that accept Kotlin suspend functions are provided for rate limiter, retry, circuit breaker, time limiter, and semaphore-based bulkheads. No extensions for thread pool bulkheads or caches are currently provided. | ||
|
||
==== Gradle | ||
|
||
Add the Kotlin module of Resilience4j to your compile dependency, along with whichever core modules are needed: | ||
|
||
[source,groovy, subs="attributes"] | ||
---- | ||
repositories { | ||
maven { url 'http://oss.jfrog.org/artifactory/oss-snapshot-local/' } | ||
mavenCentral() | ||
} | ||
dependencies { | ||
compile('io.github.resilience4j:resilience4j-kotlin:{release-version}') | ||
// also have a dependency on the core module(s) needed - for example, retry: | ||
compile('io.github.resilience4j:resilience4j-retry:{release-version}') | ||
} | ||
---- | ||
|
||
==== Basic Usage | ||
|
||
Two extension functions are declared for each of `CircuitBreaker`, `RateLimiter`, `Retry`, and `TimeLimiter`: one to execute a suspend function, and one to decorate a suspend function. | ||
|
||
[source,kotlin] | ||
---- | ||
val circuitBreaker = CircuitBreaker.ofDefaults() | ||
val result = circuitBreaker.executeSuspendFunction { | ||
// call suspending functions here | ||
} | ||
---- | ||
|
||
[source,kotlin] | ||
---- | ||
val function = circuitBreaker.decorateSuspendFunction { | ||
// call suspending functions here | ||
} | ||
val result = function() | ||
---- | ||
|
||
The suspend functions suspend where usage of the normal methods would block. For example, calls to a `RateLimiter` which need to be delayed to fit within the rate limit suspend before the given function is executed. | ||
|
||
No changes are made to the coroutine context of the given suspend functions. | ||
|
||
===== Bulkhead | ||
|
||
Decorating a suspend function with a `Bulkhead` does not add any additional suspension points. If `maxWaitTime` is non-zero, the call will *block* until the max wait time is reached or permission is obtained. For this reason, it is not recommended to use this extension function with Bulkheads with non-zero max wait times. | ||
|
||
No extension functions for thread-pool based bulkheads are provided. | ||
|
||
===== Circuit Breaker | ||
|
||
Decorating a suspend function with a `CircuitBreaker` does not add any additional suspension points. | ||
|
||
===== Rate Limiter | ||
|
||
Suspend functions decorated with a `RateLimiter` use `delay()` in order to suspend before executing the decorated function if the rate limit has been reached. | ||
|
||
===== Retry | ||
|
||
Suspend functions decorated with a `Retry` use `delay()` in order to suspend between retries. | ||
|
||
===== Time Limiter | ||
|
||
The `TimeLimiter` extension functions simply use `withTimeout()` from `kotlinx-coroutines`, using the timeout from the receiver's configuration. Specifically, this means: | ||
|
||
1. On timeout, a `TimeoutCancellationException` is raised, rather than a `TimeoutException` as with methods for non-suspending functions. | ||
1. When a timeout occurs, the coroutine is cancelled, rather than the thread being interrupted as with methods for non-suspending functions. | ||
1. After the timeout, the given block can only be stopped at a cancellable suspending function call. | ||
1. The `cancelRunningFuture` configuration setting is ignored - on timeout, the suspend function is always cancelled even if the `cancelRunningFuture` is set to `false`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
plugins { | ||
id 'org.jetbrains.kotlin.jvm' version '1.3.31' | ||
} | ||
|
||
dependencies { | ||
implementation(libraries.kotlin_stdlib) | ||
implementation(libraries.kotlin_coroutines) | ||
|
||
compileOnly(project(':resilience4j-bulkhead')) | ||
compileOnly(project(':resilience4j-circuitbreaker')) | ||
compileOnly(project(':resilience4j-ratelimiter')) | ||
compileOnly(project(':resilience4j-retry')) | ||
compileOnly(project(':resilience4j-timelimiter')) | ||
|
||
testImplementation(project(':resilience4j-bulkhead')) | ||
testImplementation(project(':resilience4j-circuitbreaker')) | ||
testImplementation(project(':resilience4j-ratelimiter')) | ||
testImplementation(project(':resilience4j-retry')) | ||
testImplementation(project(':resilience4j-timelimiter')) | ||
} | ||
|
||
compileKotlin { | ||
kotlinOptions.jvmTarget = "1.8" | ||
} | ||
|
||
compileTestKotlin { | ||
kotlinOptions.jvmTarget = "1.8" | ||
} | ||
|
||
ext.moduleName='io.github.resilience4j.kotlin' |
47 changes: 47 additions & 0 deletions
47
resilience4j-kotlin/src/main/kotlin/io/github/resilience4j/kotlin/bulkhead/Bulkhead.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
/* | ||
* | ||
* Copyright 2019: Brad Newman | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* | ||
* | ||
*/ | ||
package io.github.resilience4j.kotlin.bulkhead | ||
|
||
import io.github.resilience4j.bulkhead.Bulkhead | ||
import io.github.resilience4j.bulkhead.BulkheadConfig | ||
|
||
/** | ||
* Decorates and executes the given suspend function [block]. | ||
* | ||
* If [BulkheadConfig.maxWaitTime] is non-zero, *blocks* until the max wait time is reached or permission is obtained. | ||
* For this reason, it is not recommended to use this extension function with Bulkheads with non-zero max wait times. | ||
*/ | ||
suspend fun <T> Bulkhead.executeSuspendFunction(block: suspend () -> T): T { | ||
acquirePermission() | ||
return try { | ||
block() | ||
} finally { | ||
onComplete() | ||
} | ||
} | ||
|
||
/** | ||
* Decorates the given suspend function [block] and returns it. | ||
* | ||
* If [BulkheadConfig.maxWaitTime] is non-zero, *blocks* until the max wait time is reached or permission is obtained. | ||
* For this reason, it is not recommended to use this extension function with Bulkheads with non-zero max wait times. | ||
*/ | ||
fun <T> Bulkhead.decorateSuspendFunction(block: suspend () -> T): suspend () -> T = { | ||
executeSuspendFunction(block) | ||
} |
46 changes: 46 additions & 0 deletions
46
...e4j-kotlin/src/main/kotlin/io/github/resilience4j/kotlin/circuitbreaker/CircuitBreaker.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
/* | ||
* | ||
* Copyright 2019: Guido Pio Mariotti, Brad Newman | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* | ||
* | ||
*/ | ||
package io.github.resilience4j.kotlin.circuitbreaker | ||
|
||
import io.github.resilience4j.circuitbreaker.CircuitBreaker | ||
|
||
/** | ||
* Decorates and executes the given suspend function [block]. | ||
*/ | ||
suspend fun <T> CircuitBreaker.executeSuspendFunction(block: suspend () -> T): T { | ||
acquirePermission() | ||
val start = System.nanoTime() | ||
try { | ||
val result = block() | ||
val durationInNanos = System.nanoTime() - start | ||
onSuccess(durationInNanos) | ||
return result | ||
} catch (exception: Exception) { | ||
val durationInNanos = System.nanoTime() - start | ||
onError(durationInNanos, exception) | ||
throw exception | ||
} | ||
} | ||
|
||
/** | ||
* Decorates the given *suspend* function [block] and returns it. | ||
*/ | ||
fun <T> CircuitBreaker.decorateSuspendFunction(block: suspend () -> T): suspend () -> T = { | ||
executeSuspendFunction { block() } | ||
} |
46 changes: 46 additions & 0 deletions
46
resilience4j-kotlin/src/main/kotlin/io/github/resilience4j/kotlin/ratelimiter/RateLimiter.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
/* | ||
* | ||
* Copyright 2019: Brad Newman | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* | ||
* | ||
*/ | ||
package io.github.resilience4j.kotlin.ratelimiter | ||
|
||
import io.github.resilience4j.ratelimiter.RateLimiter | ||
import io.github.resilience4j.ratelimiter.RateLimiterConfig | ||
import io.github.resilience4j.ratelimiter.RequestNotPermitted | ||
import kotlinx.coroutines.delay | ||
import java.util.concurrent.TimeUnit | ||
|
||
/** | ||
* Decorates and executes the given suspend function [block]. | ||
* | ||
* If [RateLimiterConfig.timeoutDuration] is non-zero, the returned function suspends until a permission is available. | ||
*/ | ||
suspend fun <T> RateLimiter.executeSuspendFunction(block: suspend () -> T): T { | ||
val waitTimeNs = reservePermission() | ||
if (waitTimeNs < 0) throw RequestNotPermitted(this) | ||
delay(TimeUnit.NANOSECONDS.toMillis(waitTimeNs)) | ||
return block() | ||
} | ||
|
||
/** | ||
* Decorates the given suspend function [block] and returns it. | ||
* | ||
* If [RateLimiterConfig.timeoutDuration] is non-zero, the returned function suspends until a permission is available. | ||
*/ | ||
fun <T> RateLimiter.decorateSuspendFunction(block: suspend () -> T): suspend () -> T = { | ||
executeSuspendFunction(block) | ||
} |
59 changes: 59 additions & 0 deletions
59
resilience4j-kotlin/src/main/kotlin/io/github/resilience4j/kotlin/retry/Retry.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
/* | ||
* | ||
* Copyright 2019: Brad Newman | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
* | ||
* | ||
*/ | ||
package io.github.resilience4j.kotlin.retry | ||
|
||
import io.github.resilience4j.retry.Retry | ||
import kotlinx.coroutines.delay | ||
|
||
/** | ||
* Decorates and executes the given suspend function [block]. | ||
* | ||
* Between attempts, suspends based on the configured interval function. | ||
*/ | ||
suspend fun <T> Retry.executeSuspendFunction(block: suspend () -> T): T { | ||
val retryContext = asyncContext<T>() | ||
while (true) { | ||
try { | ||
val result = block() | ||
val delayMs = retryContext.onResult(result) | ||
if (delayMs < 1) { | ||
retryContext.onSuccess() | ||
return result | ||
} else { | ||
delay(delayMs) | ||
} | ||
} catch (e: Exception) { | ||
val delayMs = retryContext.onError(e) | ||
if (delayMs < 1) { | ||
throw e | ||
} else { | ||
delay(delayMs) | ||
} | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Decorates the given suspend function [block] and returns it. | ||
* | ||
* Between attempts, suspends based on the configured interval function. | ||
*/ | ||
fun <T> Retry.decorateSuspendFunction(block: suspend () -> T): suspend () -> T = { | ||
executeSuspendFunction(block) | ||
} |
Oops, something went wrong.