Document programmatic retry support in the reference manual
Closes gh-35436
This commit is contained in:
parent
7484b9c491
commit
bce44b007d
|
@ -1,16 +1,19 @@
|
||||||
[[resilience]]
|
[[resilience]]
|
||||||
= Resilience Features
|
= Resilience Features
|
||||||
|
|
||||||
As of 7.0, the core Spring Framework includes a couple of common resilience features,
|
As of 7.0, the core Spring Framework includes common resilience features, in particular
|
||||||
in particular `@Retryable` and `@ConcurrencyLimit` annotations for method invocations.
|
<<resilience-annotations-retryable>> and <<resilience-annotations-concurrencylimit>>
|
||||||
|
annotations for method invocations as well as <<resilience-programmatic-retry,
|
||||||
|
programmatic retry support>>.
|
||||||
|
|
||||||
|
|
||||||
[[resilience-retryable]]
|
[[resilience-annotations-retryable]]
|
||||||
== Using `@Retryable`
|
== `@Retryable`
|
||||||
|
|
||||||
`@Retryable` is a common annotation that specifies retry characteristics for an individual
|
{spring-framework-api}/resilience/annotation/Retryable.html[`@Retryable`] is an annotation
|
||||||
method (with the annotation declared at the method level), or for all proxy-invoked
|
that specifies retry characteristics for an individual method (with the annotation
|
||||||
methods in a given class hierarchy (with the annotation declared at the type level).
|
declared at the method level), or for all proxy-invoked methods in a given class hierarchy
|
||||||
|
(with the annotation declared at the type level).
|
||||||
|
|
||||||
[source,java,indent=0,subs="verbatim,quotes"]
|
[source,java,indent=0,subs="verbatim,quotes"]
|
||||||
----
|
----
|
||||||
|
@ -20,8 +23,8 @@ public void sendNotification() {
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
By default, the method invocation will be retried for any exception thrown: with at
|
By default, the method invocation will be retried for any exception thrown: with at most 3
|
||||||
most 3 retry attempts after an initial failure, and a delay of 1 second between attempts.
|
retry attempts after an initial failure, and a delay of 1 second between attempts.
|
||||||
|
|
||||||
This can be specifically adapted for every method if necessary – for example, by narrowing
|
This can be specifically adapted for every method if necessary – for example, by narrowing
|
||||||
the exceptions to retry:
|
the exceptions to retry:
|
||||||
|
@ -38,38 +41,45 @@ Or for 5 retry attempts and an exponential back-off strategy with a bit of jitte
|
||||||
|
|
||||||
[source,java,indent=0,subs="verbatim,quotes"]
|
[source,java,indent=0,subs="verbatim,quotes"]
|
||||||
----
|
----
|
||||||
@Retryable(maxAttempts = 5, delay = 100, jitter = 10, multiplier = 2, maxDelay = 1000)
|
@Retryable(
|
||||||
|
includes = MessageDeliveryException.class,
|
||||||
|
maxAttempts = 5,
|
||||||
|
delay = 100,
|
||||||
|
jitter = 10,
|
||||||
|
multiplier = 2,
|
||||||
|
maxDelay = 1000)
|
||||||
public void sendNotification() {
|
public void sendNotification() {
|
||||||
this.jmsClient.destination("notifications").send(...);
|
this.jmsClient.destination("notifications").send(...);
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
Last but not least, `@Retryable` also works for reactive methods with a reactive
|
Last but not least, `@Retryable` also works for reactive methods with a reactive return
|
||||||
return type, decorating the pipeline with Reactor's retry capabilities:
|
type, decorating the pipeline with Reactor's retry capabilities:
|
||||||
|
|
||||||
[source,java,indent=0,subs="verbatim,quotes"]
|
[source,java,indent=0,subs="verbatim,quotes"]
|
||||||
----
|
----
|
||||||
@Retryable(maxAttempts = 5, delay = 100, jitter = 10, multiplier = 2, maxDelay = 1000)
|
@Retryable(maxAttempts = 5, delay = 100)
|
||||||
public Mono<Void> sendNotification() {
|
public Mono<Void> sendNotification() {
|
||||||
return Mono.from(...); // <1>
|
return Mono.from(...); // <1>
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
<1> This raw `Mono` will get decorated with a retry spec.
|
<1> This raw `Mono` will get decorated with a retry spec.
|
||||||
|
|
||||||
For details on the various characteristics, see the available annotation attributes
|
For details on the various characteristics, see the available annotation attributes in
|
||||||
in {spring-framework-api}/resilience/annotation/Retryable.html[`@Retryable`].
|
{spring-framework-api}/resilience/annotation/Retryable.html[`@Retryable`].
|
||||||
|
|
||||||
NOTE: There a `String` variants with placeholder support available for several attributes
|
NOTE: There are `String` variants with placeholder support available for several
|
||||||
as well, as an alternative to the specifically typed annotation attributes used in the
|
attributes as well, as an alternative to the specifically typed annotation attributes used
|
||||||
above examples.
|
in the above examples.
|
||||||
|
|
||||||
|
|
||||||
[[resilience-concurrency]]
|
[[resilience-annotations-concurrencylimit]]
|
||||||
== Using `@ConcurrencyLimit`
|
== `@ConcurrencyLimit`
|
||||||
|
|
||||||
`@ConcurrencyLimit` is an annotation that specifies a concurrency limit for an individual
|
{spring-framework-api}/resilience/annotation/ConcurrencyLimit.html[`@ConcurrencyLimit`] is
|
||||||
method (with the annotation declared at the method level), or for all proxy-invoked
|
an annotation that specifies a concurrency limit for an individual method (with the
|
||||||
methods in a given class hierarchy (with the annotation declared at the type level).
|
annotation declared at the method level), or for all proxy-invoked methods in a given
|
||||||
|
class hierarchy (with the annotation declared at the type level).
|
||||||
|
|
||||||
[source,java,indent=0,subs="verbatim,quotes"]
|
[source,java,indent=0,subs="verbatim,quotes"]
|
||||||
----
|
----
|
||||||
|
@ -95,8 +105,8 @@ public void sendNotification() {
|
||||||
----
|
----
|
||||||
<1> 1 is the default, but specifying it makes the intent clearer.
|
<1> 1 is the default, but specifying it makes the intent clearer.
|
||||||
|
|
||||||
Such limiting is particularly useful with Virtual Threads where there is generally
|
Such limiting is particularly useful with Virtual Threads where there is generally no
|
||||||
no thread pool limit in place. For asynchronous tasks, this can be constrained on
|
thread pool limit in place. For asynchronous tasks, this can be constrained on
|
||||||
{spring-framework-api}/core/task/SimpleAsyncTaskExecutor.html[`SimpleAsyncTaskExecutor`].
|
{spring-framework-api}/core/task/SimpleAsyncTaskExecutor.html[`SimpleAsyncTaskExecutor`].
|
||||||
For synchronous invocations, this annotation provides equivalent behavior through
|
For synchronous invocations, this annotation provides equivalent behavior through
|
||||||
{spring-framework-api}/aop/interceptor/ConcurrencyThrottleInterceptor.html[`ConcurrencyThrottleInterceptor`]
|
{spring-framework-api}/aop/interceptor/ConcurrencyThrottleInterceptor.html[`ConcurrencyThrottleInterceptor`]
|
||||||
|
@ -104,12 +114,117 @@ which has been available since Spring Framework 1.0 for programmatic use with th
|
||||||
framework.
|
framework.
|
||||||
|
|
||||||
|
|
||||||
[[resilience-enable]]
|
[[resilience-annotations-configuration]]
|
||||||
== Configuring `@EnableResilientMethods`
|
== Enabling Resilient Methods
|
||||||
|
|
||||||
Note that like many of Spring's core annotation-based features, `@Retryable` and
|
Like many of Spring's core annotation-based features, `@Retryable` and `@ConcurrencyLimit`
|
||||||
`@ConcurrencyLimit` are designed as metadata that you can choose to honor or ignore.
|
are designed as metadata that you can choose to honor or ignore. The most convenient way
|
||||||
The most convenient way to enable actual processing of the resilience annotations
|
to enable processing of the resilience annotations is to declare
|
||||||
through AOP interception is to declare `@EnableResilientMethods` on a corresponding
|
{spring-framework-api}/resilience/annotation/EnableResilientMethods.html[`@EnableResilientMethods`]
|
||||||
configuration class. Alternatively, you may declare `RetryAnnotationBeanPostProcessor`
|
on a corresponding `@Configuration` class.
|
||||||
and/or `ConcurrencyLimitBeanPostProcessor` individually.
|
|
||||||
|
Alternatively, these annotations can be individually enabled by defining a
|
||||||
|
`RetryAnnotationBeanPostProcessor` or a `ConcurrencyLimitBeanPostProcessor` bean in the
|
||||||
|
context.
|
||||||
|
|
||||||
|
|
||||||
|
[[resilience-programmatic-retry]]
|
||||||
|
== Programmatic Retry Support
|
||||||
|
|
||||||
|
In contrast to <<resilience-annotations-retryable>> which provides a declarative approach
|
||||||
|
for specifying retry semantics for methods within beans registered in the
|
||||||
|
`ApplicationContext`,
|
||||||
|
{spring-framework-api}/core/retry/RetryTemplate.html[`RetryTemplate`] provides a
|
||||||
|
programmatic API for retrying arbitrary blocks of code.
|
||||||
|
|
||||||
|
Specifically, a `RetryTemplate` executes and potentially retries a
|
||||||
|
{spring-framework-api}/core/retry/Retryable.html[`Retryable`] operation based on a
|
||||||
|
configured {spring-framework-api}/core/retry/RetryPolicy.html[`RetryPolicy`].
|
||||||
|
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
var retryTemplate = new RetryTemplate(); // <1>
|
||||||
|
|
||||||
|
retryTemplate.execute(
|
||||||
|
() -> jmsClient.destination("notifications").send(...));
|
||||||
|
----
|
||||||
|
<1> Implicitly uses `RetryPolicy.withDefaults()`.
|
||||||
|
|
||||||
|
By default, a retryable operation will be retried for any exception thrown: with at most 3
|
||||||
|
retry attempts after an initial failure, and a delay of 1 second between attempts.
|
||||||
|
|
||||||
|
If you only need to customize the number of retry attempts, you can use the
|
||||||
|
`RetryPolicy.withMaxAttempts()` factory method as demonstrated below.
|
||||||
|
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
var retryTemplate = new RetryTemplate(RetryPolicy.withMaxAttempts(5)); // <1>
|
||||||
|
|
||||||
|
retryTemplate.execute(
|
||||||
|
() -> jmsClient.destination("notifications").send(...));
|
||||||
|
----
|
||||||
|
<1> Explicitly uses `RetryPolicy.withMaxAttempts(5)`.
|
||||||
|
|
||||||
|
If you need to narrow the types of exceptions to retry, that can be achieved via the
|
||||||
|
`includes()` and `excludes()` builder methods.
|
||||||
|
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
var retryPolicy = RetryPolicy.builder()
|
||||||
|
.includes(MessageDeliveryException.class) // <1>
|
||||||
|
.excludes(...) // <2>
|
||||||
|
.build();
|
||||||
|
|
||||||
|
var retryTemplate = new RetryTemplate(retryPolicy);
|
||||||
|
|
||||||
|
retryTemplate.execute(
|
||||||
|
() -> jmsClient.destination("notifications").send(...));
|
||||||
|
----
|
||||||
|
<1> Specify one or more exception types to include.
|
||||||
|
<2> Specify one or more exception types to exclude.
|
||||||
|
|
||||||
|
[TIP]
|
||||||
|
====
|
||||||
|
For advanced use cases, you can specify a custom `Predicate<Throwable>` via the
|
||||||
|
`predicate()` method in the `RetryPolicy.Builder`, and the predicate will be used to
|
||||||
|
determine whether to retry a failed operation based on a given `Throwable` – for example,
|
||||||
|
by checking the cause or the message of the `Throwable`.
|
||||||
|
|
||||||
|
Custom predicates can be combined with `includes` and `excludes`; however, custom
|
||||||
|
predicates will always be applied after `includes` and `excludes` have been applied.
|
||||||
|
====
|
||||||
|
|
||||||
|
The following example demonstrates how to configure a `RetryPolicy` with 5 retry attempts
|
||||||
|
and an exponential back-off strategy with a bit of jitter.
|
||||||
|
|
||||||
|
[source,java,indent=0,subs="verbatim,quotes"]
|
||||||
|
----
|
||||||
|
var retryPolicy = RetryPolicy.builder()
|
||||||
|
.includes(MessageDeliveryException.class)
|
||||||
|
.maxAttempts(5)
|
||||||
|
.delay(Duration.ofMillis(100))
|
||||||
|
.jitter(Duration.ofMillis(10))
|
||||||
|
.multiplier(2)
|
||||||
|
.maxDelay(Duration.ofSeconds(1))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
var retryTemplate = new RetryTemplate(retryPolicy);
|
||||||
|
|
||||||
|
retryTemplate.execute(
|
||||||
|
() -> jmsClient.destination("notifications").send(...));
|
||||||
|
----
|
||||||
|
|
||||||
|
[TIP]
|
||||||
|
====
|
||||||
|
A {spring-framework-api}/core/retry/RetryListener.html[`RetryListener`] can be registered
|
||||||
|
with a `RetryTemplate` to react to events published during key retry phases (before a
|
||||||
|
retry attempt, after a retry attempt, etc.), and you can compose multiple listeners via a
|
||||||
|
{spring-framework-api}/core/retry/support/CompositeRetryListener.html[`CompositeRetryListener`].
|
||||||
|
====
|
||||||
|
|
||||||
|
Although the factory methods and builder API for `RetryPolicy` cover most common
|
||||||
|
configuration scenarios, you can implement a custom `RetryPolicy` for complete control
|
||||||
|
over the types of exceptions that should trigger a retry as well as the
|
||||||
|
{spring-framework-api}/util/backoff/BackOff.html[`BackOff`] strategy to use. Note that you
|
||||||
|
can also configure a customized `BackOff` strategy via the `backOff()` method in the
|
||||||
|
`RetryPolicy.Builder`.
|
||||||
|
|
Loading…
Reference in New Issue