-
Notifications
You must be signed in to change notification settings - Fork 0
Make hw7 #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
yuuusha
wants to merge
1
commit into
hw6
Choose a base branch
from
hw7
base: hw6
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Make hw7 #8
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <project xmlns="http://maven.apache.org/POM/4.0.0" | ||
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
| <modelVersion>4.0.0</modelVersion> | ||
| <parent> | ||
| <groupId>edu.java</groupId> | ||
| <artifactId>root</artifactId> | ||
| <version>${revision}</version> | ||
| </parent> | ||
|
|
||
| <artifactId>retry</artifactId> | ||
| <version>${revision}</version> | ||
|
|
||
| <properties> | ||
| <maven.compiler.source>21</maven.compiler.source> | ||
| <maven.compiler.target>21</maven.compiler.target> | ||
| <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | ||
| </properties> | ||
| <dependencies> | ||
| <dependency> | ||
| <groupId>io.projectreactor</groupId> | ||
| <artifactId>reactor-core</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.springframework</groupId> | ||
| <artifactId>spring-webflux</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.springframework</groupId> | ||
| <artifactId>spring-context</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.springframework.boot</groupId> | ||
| <artifactId>spring-boot</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.springframework.retry</groupId> | ||
| <artifactId>spring-retry</artifactId> | ||
| </dependency> | ||
| <dependency> | ||
| <groupId>org.projectlombok</groupId> | ||
| <artifactId>lombok</artifactId> | ||
| </dependency> | ||
| </dependencies> | ||
|
|
||
| </project> |
This file contains hidden or 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,21 @@ | ||
| package edu.java; | ||
|
|
||
| import java.util.List; | ||
| import java.util.function.Predicate; | ||
| import org.springframework.web.reactive.function.client.WebClientResponseException; | ||
|
|
||
| public class ErrorFilterPredicate implements Predicate<Throwable> { | ||
| private final List<Integer> retryCodes; | ||
|
|
||
| public ErrorFilterPredicate(List<Integer> retryCodes) { | ||
| this.retryCodes = retryCodes; | ||
| } | ||
|
|
||
| @Override | ||
| public boolean test(Throwable throwable) { | ||
| if (throwable instanceof WebClientResponseException e) { | ||
| return retryCodes.contains(e.getStatusCode().value()); | ||
| } | ||
| return true; | ||
| } | ||
| } |
This file contains hidden or 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,93 @@ | ||
| package edu.java; | ||
|
|
||
| import java.time.Duration; | ||
| import java.util.function.Predicate; | ||
| import java.util.function.Supplier; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.reactivestreams.Publisher; | ||
| import org.springframework.retry.ExhaustedRetryException; | ||
| import reactor.core.publisher.Flux; | ||
| import reactor.core.publisher.Mono; | ||
| import reactor.core.scheduler.Scheduler; | ||
| import reactor.core.scheduler.Schedulers; | ||
| import reactor.util.retry.Retry; | ||
|
|
||
|
|
||
| @RequiredArgsConstructor | ||
| public class LinearRetryBackoffSpec extends Retry { | ||
| private static final Duration MAX_BACKOFF = Duration.ofMillis(Long.MAX_VALUE); | ||
| private final Duration minBackoff; | ||
| private final Duration maxBackoff; | ||
| private final double factor; | ||
| private final int maxAttempts; | ||
| private final Predicate<Throwable> errorFilter; | ||
| private final Supplier<Scheduler> schedulerSupplier; | ||
|
|
||
| public LinearRetryBackoffSpec factor(double factor) { | ||
| return new LinearRetryBackoffSpec( | ||
| this.minBackoff, | ||
| this.maxBackoff, | ||
| factor, | ||
| this.maxAttempts, | ||
| this.errorFilter, | ||
| this.schedulerSupplier | ||
| ); | ||
| } | ||
|
|
||
| public LinearRetryBackoffSpec filter(Predicate<Throwable> errorFilter) { | ||
| return new LinearRetryBackoffSpec( | ||
| this.minBackoff, | ||
| this.maxBackoff, | ||
| this.factor, | ||
| this.maxAttempts, | ||
| errorFilter, | ||
| this.schedulerSupplier | ||
| ); | ||
| } | ||
|
|
||
| public static LinearRetryBackoffSpec linear(int maxAttempts, Duration minDelay) { | ||
| return new LinearRetryBackoffSpec( | ||
| minDelay, | ||
| MAX_BACKOFF, | ||
| 1.0, | ||
| maxAttempts, | ||
| e -> true, | ||
| Schedulers::parallel | ||
| ); | ||
| } | ||
|
|
||
| @Override | ||
| public Publisher<?> generateCompanion(Flux<RetrySignal> retrySignals) { | ||
| return Flux.deferContextual(cv -> | ||
| retrySignals.contextWrite(cv) | ||
| .concatMap(retryWhenState -> { | ||
| RetrySignal copy = retryWhenState.copy(); | ||
| Throwable currentFailure = copy.failure(); | ||
| long iteration = copy.totalRetries(); | ||
| if (currentFailure == null) { | ||
| return Mono.error(new IllegalStateException( | ||
| "Retry.RetrySignal#failure() not expected to be null")); | ||
| } | ||
| if (!errorFilter.test(currentFailure)) { | ||
| return Mono.error(currentFailure); | ||
| } | ||
| if (iteration >= maxAttempts) { | ||
| return Mono.error(new ExhaustedRetryException("Retry exhausted: " + this)); | ||
| } | ||
|
|
||
| Duration nextBackoff; | ||
| try { | ||
| nextBackoff = minBackoff.multipliedBy((long) (iteration * factor)); | ||
| if (nextBackoff.compareTo(maxBackoff) > 0) { | ||
| nextBackoff = maxBackoff; | ||
| } | ||
| } catch (ArithmeticException overflow) { | ||
| nextBackoff = maxBackoff; | ||
| } | ||
|
|
||
| return Mono.delay(nextBackoff, schedulerSupplier.get()).contextWrite(cv); | ||
| }) | ||
| .onErrorStop() | ||
| ); | ||
| } | ||
| } |
This file contains hidden or 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,16 @@ | ||
| package edu.java; | ||
|
|
||
| import java.time.Duration; | ||
| import java.util.List; | ||
| import org.jetbrains.annotations.NotNull; | ||
|
|
||
| public record RetryElement( | ||
| @NotNull String target, | ||
| @NotNull String type, | ||
| int maxAttempts, | ||
| double factor, | ||
| Duration minDelay, | ||
| Duration maxDelay, | ||
| List<Integer> codes | ||
| ) { | ||
| } |
This file contains hidden or 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,41 @@ | ||
| package edu.java; | ||
|
|
||
| import edu.java.builders.ExponentialRetryBuilder; | ||
| import edu.java.builders.FixedRetryBuilder; | ||
| import edu.java.builders.LinearRetryBuilder; | ||
| import java.util.HashMap; | ||
| import java.util.Map; | ||
| import java.util.function.Function; | ||
| import lombok.experimental.UtilityClass; | ||
| import org.springframework.web.reactive.function.client.ExchangeFilterFunction; | ||
| import reactor.core.publisher.Mono; | ||
| import reactor.util.retry.Retry; | ||
|
|
||
| @UtilityClass | ||
| public class RetryFactory { | ||
|
|
||
| private static final Map<String, Function<RetryElement, Retry>> RETRY_BUILDERS = new HashMap<>(); | ||
|
|
||
| static { | ||
| RETRY_BUILDERS.put("fixed", new FixedRetryBuilder()); | ||
| RETRY_BUILDERS.put("linear", new LinearRetryBuilder()); | ||
| RETRY_BUILDERS.put("exponential", new ExponentialRetryBuilder()); | ||
| } | ||
|
|
||
| public static ExchangeFilterFunction createFilter(Retry retry) { | ||
| return (response, next) -> next.exchange(response) | ||
| .flatMap(clientResponse -> { | ||
| if (clientResponse.statusCode().isError()) { | ||
| return clientResponse.createError(); | ||
| } else { | ||
| return Mono.just(clientResponse); | ||
| } | ||
| }).retryWhen(retry); | ||
| } | ||
|
|
||
| public static Retry createRetry(RetryQueryConfiguration config, String target) { | ||
| return config.retries().stream().filter(element -> element.target().equals(target)).findFirst() | ||
| .map(element -> RETRY_BUILDERS.get(element.type()).apply(element)) | ||
| .orElseThrow(() -> new RuntimeException("Unknown target " + target)); | ||
| } | ||
| } |
This file contains hidden or 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,10 @@ | ||
| package edu.java; | ||
|
|
||
| import java.util.List; | ||
| import org.springframework.boot.context.properties.ConfigurationProperties; | ||
| import org.springframework.validation.annotation.Validated; | ||
|
|
||
| @Validated | ||
| @ConfigurationProperties(prefix = "retry-query", ignoreUnknownFields = false) | ||
| public record RetryQueryConfiguration(List<RetryElement> retries) { | ||
| } |
16 changes: 16 additions & 0 deletions
16
retry/src/main/java/edu/java/builders/ExponentialRetryBuilder.java
This file contains hidden or 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,16 @@ | ||
| package edu.java.builders; | ||
|
|
||
| import edu.java.ErrorFilterPredicate; | ||
| import edu.java.RetryElement; | ||
| import java.util.function.Function; | ||
| import reactor.util.retry.Retry; | ||
| import reactor.util.retry.RetryBackoffSpec; | ||
|
|
||
| public class ExponentialRetryBuilder implements Function<RetryElement, Retry> { | ||
| @Override | ||
| public Retry apply(RetryElement retryElement) { | ||
| return RetryBackoffSpec.backoff(retryElement.maxAttempts(), retryElement.minDelay()) | ||
| .maxBackoff(retryElement.maxDelay()) | ||
| .filter(new ErrorFilterPredicate(retryElement.codes())); | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
чтобы не создавать и хранить все политики, а использовать только нужное -
лучше сделать конфигурацию и использовать @ConditionalOnProperty для выбора политики повтора
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Я попробовал применить @ConditionalOnProperty и, мне кажется, он не очень здесь подходит, так как у меня для каждого сервиса могут быть различные типы и указывать для каждого с помощью этой аннотации не удобно. Если подскажете как красиво это сделать, то я могу попытаться, но я бы хотел оставить так
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
сейчас модульный монолит, а если далее менять архитектуру и разделять сервисы?
можешь просто сделать для каждого сервиса свою retry-конфигурацию (да, они будут почти одинаковые)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Для каждого сервиса сделал свою retry-конфигурацию в дз9