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 Features
|
||||
|
||||
As of 7.0, the core Spring Framework includes a couple of common resilience features,
|
||||
in particular `@Retryable` and `@ConcurrencyLimit` annotations for method invocations.
|
||||
As of 7.0, the core Spring Framework includes common resilience features, in particular
|
||||
<<resilience-annotations-retryable>> and <<resilience-annotations-concurrencylimit>>
|
||||
annotations for method invocations as well as <<resilience-programmatic-retry,
|
||||
programmatic retry support>>.
|
||||
|
||||
|
||||
[[resilience-retryable]]
|
||||
== Using `@Retryable`
|
||||
[[resilience-annotations-retryable]]
|
||||
== `@Retryable`
|
||||
|
||||
`@Retryable` is a common annotation that specifies retry characteristics for an individual
|
||||
method (with the 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).
|
||||
{spring-framework-api}/resilience/annotation/Retryable.html[`@Retryable`] is an annotation
|
||||
that specifies retry characteristics for an individual method (with the 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"]
|
||||
----
|
||||
|
@ -20,8 +23,8 @@ public void sendNotification() {
|
|||
}
|
||||
----
|
||||
|
||||
By default, the method invocation 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.
|
||||
By default, the method invocation 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.
|
||||
|
||||
This can be specifically adapted for every method if necessary – for example, by narrowing
|
||||
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"]
|
||||
----
|
||||
@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() {
|
||||
this.jmsClient.destination("notifications").send(...);
|
||||
}
|
||||
----
|
||||
|
||||
Last but not least, `@Retryable` also works for reactive methods with a reactive
|
||||
return type, decorating the pipeline with Reactor's retry capabilities:
|
||||
Last but not least, `@Retryable` also works for reactive methods with a reactive return
|
||||
type, decorating the pipeline with Reactor's retry capabilities:
|
||||
|
||||
[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() {
|
||||
return Mono.from(...); // <1>
|
||||
}
|
||||
----
|
||||
<1> This raw `Mono` will get decorated with a retry spec.
|
||||
|
||||
For details on the various characteristics, see the available annotation attributes
|
||||
in {spring-framework-api}/resilience/annotation/Retryable.html[`@Retryable`].
|
||||
For details on the various characteristics, see the available annotation attributes in
|
||||
{spring-framework-api}/resilience/annotation/Retryable.html[`@Retryable`].
|
||||
|
||||
NOTE: There a `String` variants with placeholder support available for several attributes
|
||||
as well, as an alternative to the specifically typed annotation attributes used in the
|
||||
above examples.
|
||||
NOTE: There are `String` variants with placeholder support available for several
|
||||
attributes as well, as an alternative to the specifically typed annotation attributes used
|
||||
in the above examples.
|
||||
|
||||
|
||||
[[resilience-concurrency]]
|
||||
== Using `@ConcurrencyLimit`
|
||||
[[resilience-annotations-concurrencylimit]]
|
||||
== `@ConcurrencyLimit`
|
||||
|
||||
`@ConcurrencyLimit` is an annotation that specifies a concurrency limit for an individual
|
||||
method (with the 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).
|
||||
{spring-framework-api}/resilience/annotation/ConcurrencyLimit.html[`@ConcurrencyLimit`] is
|
||||
an annotation that specifies a concurrency limit for an individual method (with the
|
||||
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"]
|
||||
----
|
||||
|
@ -95,8 +105,8 @@ public void sendNotification() {
|
|||
----
|
||||
<1> 1 is the default, but specifying it makes the intent clearer.
|
||||
|
||||
Such limiting is particularly useful with Virtual Threads where there is generally
|
||||
no thread pool limit in place. For asynchronous tasks, this can be constrained on
|
||||
Such limiting is particularly useful with Virtual Threads where there is generally no
|
||||
thread pool limit in place. For asynchronous tasks, this can be constrained on
|
||||
{spring-framework-api}/core/task/SimpleAsyncTaskExecutor.html[`SimpleAsyncTaskExecutor`].
|
||||
For synchronous invocations, this annotation provides equivalent behavior through
|
||||
{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.
|
||||
|
||||
|
||||
[[resilience-enable]]
|
||||
== Configuring `@EnableResilientMethods`
|
||||
[[resilience-annotations-configuration]]
|
||||
== Enabling Resilient Methods
|
||||
|
||||
Note that like many of Spring's core annotation-based features, `@Retryable` and
|
||||
`@ConcurrencyLimit` are designed as metadata that you can choose to honor or ignore.
|
||||
The most convenient way to enable actual processing of the resilience annotations
|
||||
through AOP interception is to declare `@EnableResilientMethods` on a corresponding
|
||||
configuration class. Alternatively, you may declare `RetryAnnotationBeanPostProcessor`
|
||||
and/or `ConcurrencyLimitBeanPostProcessor` individually.
|
||||
Like many of Spring's core annotation-based features, `@Retryable` and `@ConcurrencyLimit`
|
||||
are designed as metadata that you can choose to honor or ignore. The most convenient way
|
||||
to enable processing of the resilience annotations is to declare
|
||||
{spring-framework-api}/resilience/annotation/EnableResilientMethods.html[`@EnableResilientMethods`]
|
||||
on a corresponding `@Configuration` class.
|
||||
|
||||
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