Mastering API Retry Strategies: 8 Proven Java Implementations
This article explains why retry mechanisms are essential for cross‑border services, then walks through eight practical Java approaches—including simple loops, recursion, Apache HttpClient settings, Spring Retry, Resilience4j, custom utilities, thread‑pool async retries, and message‑queue based retries—plus best‑practice tips.
Retry Mechanism Implementation
In cross‑border scenarios third‑party servers are distributed worldwide, so network failures are common when calling external APIs. Adding a retry mechanism helps improve reliability.
1. Loop Retry
The simplest method is to wrap the request in a for loop and retry until success or a maximum count is reached.
int retryTimes = 3;
for (int i = 0; i < retryTimes; i++) {
try {
// request API code
break;
} catch (Exception e) {
// handle exception
Thread.sleep(1000); // delay 1 second before retry
}
}2. Recursive Retry
Recursion can achieve the same effect by calling the method itself on failure.
public void requestWithRetry(int retryTimes) {
if (retryTimes <= 0) return;
try {
// request API code
} catch (Exception e) {
Thread.sleep(1000);
requestWithRetry(retryTimes - 1);
}
}3. Built‑in HttpClient Retry
Apache HttpClient provides built‑in retry handlers.
CloseableHttpClient httpClient = HttpClients.custom()
.setRetryHandler(new DefaultHttpRequestRetryHandler(3, true))
.build();For version 5.x use .setRetryStrategy(...) with DefaultHttpRequestRetryStrategy.
4. Spring Retry Library
Add the dependency:
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.3.1</version>
</dependency>Configure a RetryTemplate with a SimpleRetryPolicy (max attempts) and a FixedBackOffPolicy (delay), then execute the request:
RetryTemplate retryTemplate = new RetryTemplate();
RetryPolicy retryPolicy = new SimpleRetryPolicy(3);
retryTemplate.setRetryPolicy(retryPolicy);
FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();
backOffPolicy.setBackOffPeriod(1000);
retryTemplate.setBackOffPolicy(backOffPolicy);
retryTemplate.execute(context -> {
// request API code
return null;
});Or use the annotation‑driven approach:
@Retryable(maxAttempts = 3, backoff = @Backoff(delay = 1000))
public void request() {
// request API code
}5. Resilience4j
Resilience4j offers a lightweight retry module.
RetryRegistry retryRegistry = RetryRegistry.ofDefaults();
RetryConfig config = RetryConfig.custom()
.maxAttempts(3)
.waitDuration(Duration.ofMillis(1000))
.retryOnResult(response -> response.getStatus() == 500)
.retryOnException(e -> e instanceof WebServiceException)
.build();
Retry retry = retryRegistry.retry("name", config);
CheckedFunction0<String> retryableSupplier = Retry.decorateCheckedSupplier(
retry, () -> "result");Resilience4j also supports the @Retryable annotation in Spring Boot.
6. Custom Retry Utility
Define an abstract Callback with a doProcess() method returning a RetryResult. RetryExecutor.execute() loops until the callback signals no further retry.
public abstract class Callback {
public abstract RetryResult doProcess();
}
public class RetryResult {
private Boolean isRetry;
private Object obj;
public static RetryResult ofResult(Boolean isRetry, Object obj) { return new RetryResult(isRetry, obj); }
public static RetryResult ofResult(Boolean isRetry) { return new RetryResult(isRetry, null); }
// getters omitted
}
public class RetryExecutor {
public static Object execute(int retryCount, Callback callback) {
for (int cur = 0; cur < retryCount; cur++) {
RetryResult result = callback.doProcess();
if (result.isRetry()) continue;
return result.getObj();
}
return null;
}
}Usage example:
int maxRetryCount = 3;
Object result = RetryExecutor.execute(maxRetryCount, new Callback() {
@Override
public RetryResult doProcess() {
// request logic
// return RetryResult.ofResult(true) to retry
// return RetryResult.ofResult(false, result) on success
}
});7. Asynchronous Retry with ThreadPoolExecutor
int maxRetryTimes = 3;
int currentRetryTimes = 0;
ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>());
Callable<String> task = () -> {
// request API code
return "result";
};
Future<String> future;
while (currentRetryTimes < maxRetryTimes) {
try {
future = executor.submit(task);
String result = future.get();
break;
} catch (Exception e) {
currentRetryTimes++;
Thread.sleep(1000);
}
}8. Message‑Queue Retry (RocketMQ)
@Component
@RocketMQMessageListener(topic = "myTopic", consumerGroup = "myConsumerGroup")
public class MyConsumer implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
try {
// request API code
} catch (Exception e) {
DefaultMQProducer producer = new DefaultMQProducer("myProducerGroup");
producer.setNamesrvAddr("127.0.0.1:9876");
try {
producer.start();
Message msg = new Message("myTopic", "myTag", message.getBytes());
producer.send(msg);
} catch (Exception ex) {
// handle send failure
} finally {
producer.shutdown();
}
}
}
}Best Practices and Precautions
Set reasonable retry counts and intervals to avoid overwhelming the downstream service.
Ensure the API is idempotent; otherwise, repeated writes may cause data inconsistency.
Handle concurrency properly—use locks or distributed locks when multiple threads may retry the same request.
Classify exceptions: network timeouts are usually retry‑able, while database or file‑system errors may need different handling.
Avoid infinite loops; always enforce a maximum retry limit to prevent resource exhaustion.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Su San Talks Tech
Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
