From 5cb5af553e113cbcd44020ef5c4c786a239d0596 Mon Sep 17 00:00:00 2001 From: Nicola Dardanis Date: Tue, 14 Feb 2023 12:56:07 +0100 Subject: [PATCH] feat: enhance Feign utility using resiliency4j retries --- core/pom.xml | 8 +++ .../com/adobe/aio/util/feign/FeignUtil.java | 52 +++++++++++++++++-- .../adobe/aio/ims/feign/FeignImsService.java | 1 + pom.xml | 12 +++++ 4 files changed, 70 insertions(+), 3 deletions(-) diff --git a/core/pom.xml b/core/pom.xml index 2531c941..5052b74b 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -54,6 +54,14 @@ io.github.openfeign.form feign-form + + io.github.resilience4j + resilience4j-feign + + + io.github.resilience4j + resilience4j-retry + io.openapitools.jackson.dataformat jackson-dataformat-hal diff --git a/core/src/main/java/com/adobe/aio/util/feign/FeignUtil.java b/core/src/main/java/com/adobe/aio/util/feign/FeignUtil.java index b3bac24c..5ad38ce4 100644 --- a/core/src/main/java/com/adobe/aio/util/feign/FeignUtil.java +++ b/core/src/main/java/com/adobe/aio/util/feign/FeignUtil.java @@ -15,17 +15,26 @@ import feign.Feign; import feign.Logger.Level; import feign.Request; +import feign.RetryableException; +import feign.Retryer; import feign.form.FormEncoder; import feign.jackson.JacksonDecoder; import feign.jackson.JacksonEncoder; import feign.optionals.OptionalDecoder; import feign.slf4j.Slf4jLogger; +import io.github.resilience4j.core.IntervalFunction; +import io.github.resilience4j.feign.FeignDecorators; +import io.github.resilience4j.feign.Resilience4jFeign; +import io.github.resilience4j.retry.Retry; +import io.github.resilience4j.retry.RetryConfig; import java.util.concurrent.TimeUnit; +import java.util.function.Function; public class FeignUtil { public static final int DEFAULT_CONNECT_TIMEOUT_IN_SECONDS = 10; public static final int DEFAULT_READ_TIMEOUT_IN_SECONDS = 60; + private static final long DEFAULT_RETRY_INITIAL_INTERVAL_IN_MILLISECONDS = 2000; private FeignUtil() { } @@ -35,10 +44,39 @@ private FeignUtil() { * our global read and time out options */ public static Feign.Builder getBaseBuilder() { - return Feign.builder() + return getBaseBuilder(DEFAULT_RETRY_INITIAL_INTERVAL_IN_MILLISECONDS); + } + + public static Feign.Builder getBaseBuilder(long initialIntervalMillis) { + final IntervalFunction exponentialBackoff = IntervalFunction + .ofExponentialBackoff(initialIntervalMillis, 2.0); + FeignDecorators decorators = FeignDecorators.builder() + .withRetry(Retry.of("default", RetryConfig.custom() + .maxAttempts(3) + .retryOnException((t) -> t instanceof RetryableException) + .intervalBiFunction((attempt, result) -> result + .bimap( + e -> { + if (e instanceof RetryableException) { + RetryableException ex = (RetryableException) e; + if (ex.retryAfter() != null) { + long interval = System.currentTimeMillis() - ex.retryAfter().getTime(); + return Math.max(exponentialBackoff.apply(attempt), interval); + } + } + return exponentialBackoff.apply(attempt); + }, + r -> exponentialBackoff.apply(attempt) + ) + .fold(Function.identity(), Function.identity()) + ) + .build()) + ).build(); + return Resilience4jFeign.builder(decorators) .logger(new Slf4jLogger()) //.logLevel(Level.BASIC) .logLevel(Level.NONE) + .retryer(Retryer.NEVER_RETRY) //.logLevel(Level.FULL) // use this instead when debugging .decode404() .errorDecoder(new IOErrorDecoder()) @@ -51,7 +89,11 @@ public static Feign.Builder getBaseBuilder() { * logger and our global read and time out options */ public static Feign.Builder getDefaultBuilder() { - return getBaseBuilder() + return getDefaultBuilder(DEFAULT_RETRY_INITIAL_INTERVAL_IN_MILLISECONDS); + } + + public static Feign.Builder getDefaultBuilder(long initialIntervalMillis) { + return getBaseBuilder(initialIntervalMillis) .decoder(new OptionalDecoder(new JacksonDecoder(JacksonUtil.DEFAULT_OBJECT_MAPPER))) .encoder(new JacksonEncoder(JacksonUtil.DEFAULT_OBJECT_MAPPER)); @@ -62,7 +104,11 @@ public static Feign.Builder getDefaultBuilder() { * global read and time out options, and form encoder */ public static Feign.Builder getBuilderWithFormEncoder() { - return getBaseBuilder() + return getBaseBuilder(DEFAULT_RETRY_INITIAL_INTERVAL_IN_MILLISECONDS); + } + + public static Feign.Builder getBuilderWithFormEncoder(long initialIntervalMillis) { + return getBaseBuilder(initialIntervalMillis) .decoder(new OptionalDecoder(new JacksonDecoder(JacksonUtil.DEFAULT_OBJECT_MAPPER))) .encoder(new FormEncoder()); } diff --git a/ims/src/main/java/com/adobe/aio/ims/feign/FeignImsService.java b/ims/src/main/java/com/adobe/aio/ims/feign/FeignImsService.java index f5c0ec97..faba4fbe 100644 --- a/ims/src/main/java/com/adobe/aio/ims/feign/FeignImsService.java +++ b/ims/src/main/java/com/adobe/aio/ims/feign/FeignImsService.java @@ -17,6 +17,7 @@ import com.adobe.aio.ims.api.ImsApi; import com.adobe.aio.ims.model.AccessToken; import com.adobe.aio.util.feign.FeignUtil; +import feign.Retryer; public class FeignImsService implements ImsService { diff --git a/pom.xml b/pom.xml index 432a0a64..7e151051 100644 --- a/pom.xml +++ b/pom.xml @@ -113,6 +113,7 @@ 11.2 3.8.0 + 1.7.0 @@ -212,6 +213,17 @@ ${feign-form.version} + + io.github.resilience4j + resilience4j-feign + ${resilience4j.version} + + + io.github.resilience4j + resilience4j-retry + ${resilience4j.version} + + junit junit