Skip to content

Commit

Permalink
Issue ReactiveX#348: Spring boot2 support for retry
Browse files Browse the repository at this point in the history
* Add response predicate to retry sync and async for enhancement ReactiveX#259

* ReactiveX#348 add sync retry spring boot annotation and config support for spring boot 1 and 2

* ReactiveX#348 add sync retry spring boot annotation and config support for spring boot 1 and 2

* ReactiveX#348 adding java doc

* ReactiveX#348 adding java doc

* ReactiveX#348 adding spring override bean option for the retry spring boot starters

* ReactiveX#348 adding spring async retry aspect support

* ReactiveX#348 adding annotation validation protection of using retry and async retry annotation together in the class level

* ReactiveX#348 updating java doc

* ReactiveX#348 adding the new prefix of async retry metrics and fixing the merge conflicts

* ReactiveX#348 covering review comments

* ReactiveX#348 removing unneeded lines

* ReactiveX#348 adding the updated spring boot documentation for the retry spring boot usage for spring boot 1 and 2

* ReactiveX#348 documentation review comments

* ReactiveX#348 documentation review comments and removing health indicators for retry support in spring boot

* ReactiveX#348 documentation review comments
  • Loading branch information
Romeh authored and RobWin committed Mar 12, 2019
1 parent 6b7078f commit 1e063a4
Show file tree
Hide file tree
Showing 49 changed files with 3,325 additions and 250 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2019 Mahmoud Romeh
*
* 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.retry.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* This annotation can be applied to a class or a specific method.
* Applying it on a class is equivalent to applying it on all its public methods.
* The annotation enables backend retry for all methods where it is applied.
* Backend retry is performed via a retry
*/
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD, ElementType.TYPE})
@Documented
public @interface AsyncRetry {

/**
* Name of the async retry.
*
* @return return name of the async retry
*/
String name();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2019 Mahmoud Romeh
*
* 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.retry.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* This annotation can be applied to a class or a specific method.
* Applying it on a class is equivalent to applying it on all its public methods.
* The annotation enables backend retry for all methods where it is applied.
* Backend retry is performed via a retry
*/
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD, ElementType.TYPE})
@Documented
public @interface Retry {

/**
* Name of the sync retry.
*
* @return the name of the sync retry.
*/
String name();
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,30 @@ dependencies {
Spring Boot Actuator health information can be used to check the status of your running application.
It is often used by monitoring software to alert someone if a production system has serious issues.

==== Retry
When you enable retry, metrics are automatically published on the metrics endpoint.

For example:

[source,json]
----
{
"names": [
"resilience4j.retry.backendA.failedCallsWithoutRetry",
"resilience4j.retry.backendA.failedCallsWithRetry",
"resilience4j.retry.backendA.successCalls",
"resilience4j.retry.backendA.successCallsWithRetry",
"resilience4j.asyncRetry.backendA.failedCallsWithoutRetry",
"resilience4j.asyncRetry.backendB.failedCallsWithRetry",
"resilience4j.asyncRetry.backendB.successCalls",
"resilience4j.asyncRetry.backendB.successCallsWithRetry"
]
}
----

When you want to publish Retry/AsyncRetry endpoints on the Prometheus endpoint, you have to add the dependency `io.micrometer:micrometer-registry-prometheus`.
and you have same metrics exposed there , check circuit breaker below for more information about the example.

==== CircuitBreaker
This demo publishes the status and metrics of all CircuitBreakers via a custom `CircuitBreakerHealthIndicator`.
A closed CircuitBreaker state is mapped to UP, an open state to DOWN and a half-open state to UNKNOWN.
Expand Down Expand Up @@ -133,6 +157,67 @@ For example:

==== Configuration

===== Retry
You can configure your Retries in Spring Boot's `application.yml` config file.
For example
[source,yaml]
----
resilience4j.retry:
retryAspectOrder: 399
backends:
retryBackendA:
maxRetryAttempts: 3
waitDuration: 600
eventConsumerBufferSize: 100
enableExponentialBackoff: false
exponentialBackoffMultiplier: 2
enableRandomizedWait: false
randomizedWaitFactor: 2
retryExceptionPredicate: io.github.resilience4j.circuitbreaker.RecordFailurePredicate
retryExceptions:
- java.io.IOException
ignoreExceptions:
- io.github.resilience4j.circuitbreaker.IgnoredException
----
The rules for Retry configuration :

- By default the same back end configuration will be used for sync and async retry configuration if not defined otherwise.
- enableRandomizedWait and enableExponentialBackoff is false by default.
- You can not enable both enableRandomizedWait and enableExponentialBackoff , validation exception will be thrown if it happen.
- If exponentialBackoffMultiplier is not provided if enableExponentialBackoff is enabled , default ExponentialBackoff will be used , same story for enableRandomizedWait.

The rules for Retry spring annotation usage :

- You can use the same back-end configuration for both sync and async retry if you use both annotations in for the same backed method level wise only ,
if you mix annotations between class level and method level on the same back-end class , validation exception will be thrown
- For `AsyncRetry` annotation , please make sure the return type is instance of Java `CompletionStage` otherwise runtime exception will be thrown

Code example of retry and async retry annotation usage in Java Spring component :
[source,java]
----
@component
public class RetryDummyServiceImpl implements RetryDummyService {
@Retry(name = RetryDummyService.BACKEND)
@Override
public void doSomething(boolean throwBackendTrouble) throws IOException {
if (throwBackendTrouble) {
throw new IOException("Test Message");
}
}
@AsyncRetry(name = RetryDummyService.BACKEND)
@Override
public CompletionStage<String> doSomethingAsync(boolean throwException) throws IOException {
if (throwException) {
throw new IOException("Test Message");
} else {
return CompletableFuture.supplyAsync(() -> "test");
}
}
}
----
===== CircuitBreaker
You can configure your CircuitBreakers in Spring Boot's `application.yml` config file.
For example
Expand Down Expand Up @@ -197,6 +282,82 @@ WARNING: Please be careful changing of `CircuitBreaker`/`RateLimiter` ordering c

==== Event Monitoring

===== Retry

The emitted Retry events are stored in a separate circular event consumer buffers. The size of a event consumer buffer can be configured per Retry in the application.yml file (eventConsumerBufferSize).
The demo adds a custom Spring Boot Actuator endpoint which can be used to monitor the emitted events of your Retries.
The endpoint `/management/retries` lists the names of all Retries instances.
For example:
----
{
"retries": [
"retryBackendA",
"retryBackendA"
]
}
----

The endpoint `/management/retries/events` lists the latest 100 emitted events of all Retries instances.

----
{
"retryEvents": [
{
"retryName": "retryBackendC",
"type": "RETRY",
"creationTime": "2019-03-11T17:32:49.648+01:00[Europe/Brussels]",
"errorMessage": "java.io.IOException: Test Message",
"numberOfAttempts": 1
},
{
"retryName": "retryBackendA",
"type": "RETRY",
"creationTime": "2019-03-11T17:32:50.259+01:00[Europe/Brussels]",
"errorMessage": "java.io.IOException: Test Message",
"numberOfAttempts": 2
},
{
"retryName": "retryBackendA",
"type": "ERROR",
"creationTime": "2019-03-11T17:32:50.866+01:00[Europe/Brussels]",
"errorMessage": "java.io.IOException: Test Message",
"numberOfAttempts": 3
}
]
}
----

The endpoint `/management/retries/events/{retryrName}` lists the latest emitted events of a specific Retry.
For example `/management/retries/events/retryBackendA`:

----
{
"retryEvents": [
{
"retryName": "retryBackendA",
"type": "RETRY",
"creationTime": "2019-03-11T17:32:49.648+01:00[Europe/Brussels]",
"errorMessage": "java.io.IOException: Test Message",
"numberOfAttempts": 1
},
{
"retryName": "retryBackendA",
"type": "RETRY",
"creationTime": "2019-03-11T17:32:50.259+01:00[Europe/Brussels]",
"errorMessage": "java.io.IOException: Test Message",
"numberOfAttempts": 2
},
{
"retryName": "retryBackendA",
"type": "ERROR",
"creationTime": "2019-03-11T17:32:50.866+01:00[Europe/Brussels]",
"errorMessage": "java.io.IOException: Test Message",
"numberOfAttempts": 3
}
]
}
----

===== CircuitBreaker

The emitted CircuitBreaker events are stored in a separate circular event consumer buffers. The size of a event consumer buffer can be configured per CircuitBreaker in the application.yml file (eventConsumerBufferSize).
Expand Down
Loading

0 comments on commit 1e063a4

Please sign in to comment.