doc: 阅读Retry文档
This commit is contained in:
@@ -32,6 +32,12 @@
|
||||
- [decorate and execute a functional interface](#decorate-and-execute-a-functional-interface-2)
|
||||
- [consume emitted RegistryEvents](#consume-emitted-registryevents-2)
|
||||
- [consume emitted RateLimiterEvents](#consume-emitted-ratelimiterevents)
|
||||
- [Retry](#retry)
|
||||
- [Create a RetryRegistry](#create-a-retryregistry)
|
||||
- [Create and configure Retry](#create-and-configure-retry)
|
||||
- [Decorate and execute a functional interface](#decorate-and-execute-a-functional-interface-3)
|
||||
- [consume emitted RegistryEvents](#consume-emitted-registryevents-3)
|
||||
- [use custom IntervalFunction](#use-custom-intervalfunction)
|
||||
|
||||
|
||||
# CircuitBreaker
|
||||
@@ -704,3 +710,155 @@ ReactorAdapter.toFlux(rateLimiter.getEventPublisher())
|
||||
.filter(event -> event.getEventType() == FAILED_ACQUIRE)
|
||||
.subscribe(event -> logger.info(...))
|
||||
```
|
||||
|
||||
## Retry
|
||||
### Create a RetryRegistry
|
||||
和CircuitBreaker module类似,该module也提供了in-memory RetryRegistry,用于对Retry对象进行管理。
|
||||
|
||||
```java
|
||||
RetryRegistry retryRegistry = RetryRegistry.ofDefaults();
|
||||
```
|
||||
|
||||
### Create and configure Retry
|
||||
可以提供一个自定义的global RetryConfig,可以使用builder来创建RetryConfig。
|
||||
|
||||
RetryConfig支持如下配置:
|
||||
- `maxAttempts`:
|
||||
- `default value`: 3
|
||||
- `description`:该参数代表最大尝试次数(包含最初始的调用,最初始的调用为first attemp)
|
||||
- `waitDuration`:
|
||||
- `default value`: 500 [ms]
|
||||
- `description`: 在多次retry attempts之间的固定等待间隔
|
||||
- `intervalFunction`:
|
||||
- `defaultValue`: `numOfAttempts -> waitDuration`
|
||||
- `description`: 该参数对应的function用于在失败后修改等待时间,默认情况下每次失败后等待时间是固定的,都是waitDuration
|
||||
- `intervalBiFunction`:
|
||||
- `default value`: `(int numOfAttempts, Either<Throwable, T> result)->waitDuration`
|
||||
- `description`: 该function用于在failure后修改等待时间间隔,即基于attempt number和result/exception来计算等待时间间隔。当同时使用`intervalFunction`和`intervalBiFunction`时,会抛出异常
|
||||
- `retryOnResultPredicate`:
|
||||
- `default value`: `result -> false`
|
||||
- `description`: 该参数用于配置predicate,用于判断result是否应该被重试。如果result应当被重试,那么返回true,否则返回false
|
||||
- `retryOnExceptionPredicate`:
|
||||
- `default`: `throwable -> true`
|
||||
- `description`: 该参数用于判断是否exception应当被重试。如果exception应当被重试,predicate返回true,否则返回false
|
||||
- `retryExceptions`:
|
||||
- `default value`: empty
|
||||
- `description`: 配置exception list,其将被记录为failure并且应当被重试。
|
||||
- `ignoreExceptions`:
|
||||
- `default value`: empty
|
||||
- `descritpion`: 配置exception list,该列表中的异常会被ignore并且不会重试
|
||||
- `failAfterMaxAttempts`:
|
||||
- `default value`: false
|
||||
- `description`: 该参数用于启用和关闭`MaxRetriesExceededException`的抛出,当Retry达到配置的maxAttempts后,若result没有通过`retryOnResultPredicate`,则会根据`failAfterMaxAttempts`来重试
|
||||
|
||||
> 在抛出异常后,是否重试的逻辑如下:
|
||||
> - 根据predicate判断该异常是否应该重试,predicate逻辑判断流程如下
|
||||
> - 如果异常位于ignoreExceptions中,则不应重试
|
||||
> - 如果异常位于retryExceptions中,则predicate返回为true
|
||||
> - 如果异常不位于retryExceptions中,则根据retryOnExceptionPredicate来判断是否异常应当触发重试
|
||||
> - 如果上述的predicate判断异常应该被重试,那么再递增重试次数,判断当前重试是否超过maxAttempts
|
||||
> - 如果没有超过,则在等待interval后触发重试
|
||||
> - 如果超过maxAttempts规定的上限,则不再重试直接抛出异常
|
||||
|
||||
> 在未抛出异常时,判断是否重试的逻辑如下:
|
||||
> - 首先,根据`retryOnResultPredicate`判断当前返回结果是否应当触发重试,如果不应触发重试,则流程结束
|
||||
> - 如果应当触发重试,则增加当前的重试次数,并和maxAttempts进行比较
|
||||
> - 如果当前重试次数未超过maxAttempts,则在等待interval后触发重试
|
||||
> - 如果重试次数超过maxAttempts规定的值,那么将根据failAfterMaxAttempts来决定是否抛出异常。当failAfterMaxAttempts为true时,抛出异常;当为false时,不跑出异常。默认不会抛出异常。
|
||||
|
||||
创建RetryConfig的默认示例如下:
|
||||
```java
|
||||
RetryConfig config = RetryConfig.custom()
|
||||
.maxAttempts(2)
|
||||
.waitDuration(Duration.ofMillis(1000))
|
||||
.retryOnResult(response -> response.getStatus() == 500)
|
||||
.retryOnException(e -> e instanceof WebServiceException)
|
||||
.retryExceptions(IOException.class, TimeoutException.class)
|
||||
.ignoreExceptions(BusinessException.class, OtherBusinessException.class)
|
||||
.failAfterMaxAttempts(true)
|
||||
.build();
|
||||
|
||||
// Create a RetryRegistry with a custom global configuration
|
||||
RetryRegistry registry = RetryRegistry.of(config);
|
||||
|
||||
// Get or create a Retry from the registry -
|
||||
// Retry will be backed by the default config
|
||||
Retry retryWithDefaultConfig = registry.retry("name1");
|
||||
|
||||
// Get or create a Retry from the registry,
|
||||
// use a custom configuration when creating the retry
|
||||
RetryConfig custom = RetryConfig.custom()
|
||||
.waitDuration(Duration.ofMillis(100))
|
||||
.build();
|
||||
|
||||
Retry retryWithCustomConfig = registry.retry("name2", custom);
|
||||
```
|
||||
### Decorate and execute a functional interface
|
||||
Retry可以针对`callable, supplier, runnable, consumer, checkedrunnable, checkedsupplier, checkedconsumer, completionstage`进行decorate,使用示例如下:
|
||||
```java
|
||||
// Given I have a HelloWorldService which throws an exception
|
||||
HelloWorldService helloWorldService = mock(HelloWorldService.class);
|
||||
given(helloWorldService.sayHelloWorld())
|
||||
.willThrow(new WebServiceException("BAM!"));
|
||||
|
||||
// Create a Retry with default configuration
|
||||
Retry retry = Retry.ofDefaults("id");
|
||||
// Decorate the invocation of the HelloWorldService
|
||||
CheckedFunction0<String> retryableSupplier = Retry
|
||||
.decorateCheckedSupplier(retry, helloWorldService::sayHelloWorld);
|
||||
|
||||
// When I invoke the function
|
||||
Try<String> result = Try.of(retryableSupplier)
|
||||
.recover((throwable) -> "Hello world from recovery function");
|
||||
|
||||
// Then the helloWorldService should be invoked 3 times
|
||||
BDDMockito.then(helloWorldService).should(times(3)).sayHelloWorld();
|
||||
// and the exception should be handled by the recovery function
|
||||
assertThat(result.get()).isEqualTo("Hello world from recovery function");
|
||||
```
|
||||
|
||||
### consume emitted RegistryEvents
|
||||
可以向RetryRegistry注册监听,消费Retry的create, replace, delete事件
|
||||
```java
|
||||
RetryRegistry registry = RetryRegistry.ofDefaults();
|
||||
registry.getEventPublisher()
|
||||
.onEntryAdded(entryAddedEvent -> {
|
||||
Retry addedRetry = entryAddedEvent.getAddedEntry();
|
||||
LOG.info("Retry {} added", addedRetry.getName());
|
||||
})
|
||||
.onEntryRemoved(entryRemovedEvent -> {
|
||||
Retry removedRetry = entryRemovedEvent.getRemovedEntry();
|
||||
LOG.info("Retry {} removed", removedRetry.getName());
|
||||
});
|
||||
```
|
||||
|
||||
### use custom IntervalFunction
|
||||
如果不想使用fixed wait duration,可以自定义`IntervalFunction`,该函数可以在每次attempt时独立计算wait duration。resilience4j支持一些工厂方法用于创建IntervalFunction,示例如下
|
||||
```java
|
||||
IntervalFunction defaultWaitInterval = IntervalFunction
|
||||
.ofDefaults();
|
||||
|
||||
// This interval function is used internally
|
||||
// when you only configure waitDuration
|
||||
IntervalFunction fixedWaitInterval = IntervalFunction
|
||||
.of(Duration.ofSeconds(5));
|
||||
|
||||
IntervalFunction intervalWithExponentialBackoff = IntervalFunction
|
||||
.ofExponentialBackoff();
|
||||
|
||||
IntervalFunction intervalWithCustomExponentialBackoff = IntervalFunction
|
||||
.ofExponentialBackoff(IntervalFunction.DEFAULT_INITIAL_INTERVAL, 2d);
|
||||
|
||||
IntervalFunction randomWaitInterval = IntervalFunction
|
||||
.ofRandomized();
|
||||
|
||||
// Overwrite the default intervalFunction with your custom one
|
||||
RetryConfig retryConfig = RetryConfig.custom()
|
||||
.intervalFunction(intervalWithExponentialBackoff)
|
||||
.build();
|
||||
```
|
||||
> intervalFunction和intervalBiFunction不能同时指定,同时指定时会抛出异常。
|
||||
>
|
||||
> 如果指定了intervalFunction,那么在通过builder创建RetryConfig时,会自动通过intervalFunction给intervalBiFunction也赋值。
|
||||
>
|
||||
> 如果指定了intervalFunction或intervalBiFunction中任一,则使用指定的函数来计算waitDuration,当二者都没有指定时,则waitDuration固定为waitDuration
|
||||
Reference in New Issue
Block a user