8 Effective Retry Strategies for Java APIs
This article presents eight practical retry mechanisms for Java applications—including loop, recursion, HttpClient configuration, Spring Retry, Resilience4j, custom utilities, asynchronous thread‑pool retries, and message‑queue based retries—along with code examples, configuration snippets, and best‑practice guidelines to help developers handle transient network failures reliably.
Retry Mechanism Implementation
1. Loop Retry
This is the simplest approach: wrap the request in a for loop and retry until success or the maximum number of attempts is reached, adding a delay with Thread.sleep() after each failure.
int retryTimes = 3;
for (int i = 0; i < retryTimes; i++) {
try {
// request code
break;
} catch (Exception e) {
// handle exception
Thread.sleep(1000); // wait 1 second before next try
}
}2. Recursive Retry
Define a method that calls itself when an exception occurs, decreasing the remaining retry count each time.
public void requestWithRetry(int retryTimes) {
if (retryTimes <= 0) return;
try {
// request code
} catch (Exception e) {
Thread.sleep(1000);
requestWithRetry(retryTimes - 1);
}
}3. HttpClient Built‑in Retry
Many HTTP clients provide built‑in retry handlers. For Apache HttpClient you can configure a HttpRequestRetryHandler (4.5+) or RetryStrategy (5.x) with a maximum of three attempts.
CloseableHttpClient httpClient = HttpClients.custom()
.setRetryHandler(new DefaultHttpRequestRetryHandler(3, true))
.build(); CloseableHttpClient httpClient = HttpClients.custom()
.setRetryStrategy(new DefaultHttpRequestRetryStrategy(3, NEG_ONE_SECOND))
.build();4. Spring Retry Library
Add the spring-retry dependency and use either RetryTemplate programmatically or the @Retryable annotation to declaratively retry a method.
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>1.3.1</version>
</dependency>Programmatic example:
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 code
return null;
});Annotation example:
@Retryable(value = Exception.class, maxAttempts = 3, backoff = @Backoff(delay = 1000))
public void request() {
// request code
}5. Resilience4j
Resilience4j offers a lightweight retry module that can be used via code or annotations.
RetryRegistry retryRegistry = RetryRegistry.ofDefaults();
RetryConfig config = RetryConfig.custom()
.maxAttempts(3)
.waitDuration(Duration.ofMillis(1000))
.retryOnResult(response -> response.getStatus() == 500)
.retryOnException(e -> e instanceof WebServiceException)
.ignoreExceptions(BusinessException.class, OtherBusinessException.class)
.failAfterMaxAttempts(true)
.build();
Retry retry = retryRegistry.retry("myRetry", config);
CheckedFunction0<String> retryableSupplier = Retry.decorateCheckedSupplier(
retry, () -> "result");Annotation example:
@Service
public class MyService {
@Retryable(value = MyException.class, maxAttempts = 3, backoff = @Backoff(delay = 1000))
public void doSomething() {
// request code
}
}6. Custom Retry Utility
Define an abstract Callback with a doProcess() method returning a RetryResult. Use a RetryExecutor to loop until the callback signals success.
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);
}
}
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;
}
}7. Asynchronous Thread‑Pool Retry
Submit the request as a Callable to a
ThreadPoolExecutor** and retry the task while monitoring the <code>Futureresult.
int maxRetryTimes = 3;
int currentRetryTimes = 0;
ThreadPoolExecutor executor = new ThreadPoolExecutor(
10, 10, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());
Callable<String> task = () -> {
// request 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)
When a request fails, re‑publish the payload to a RocketMQ topic so that a consumer can retry later, ensuring reliability even if the service is temporarily down.
@Component
@RocketMQMessageListener(topic = "myTopic", consumerGroup = "myConsumerGroup")
public class MyConsumer implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
try {
// request 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 Cautions
Set reasonable retry counts and intervals to avoid overwhelming the target service.
Consider idempotency; avoid duplicate writes when the downstream service is not idempotent.
Handle concurrency to prevent duplicate requests, using locks or distributed locks if needed.
Classify exceptions: retry only transient errors (e.g., timeouts, connection issues) and treat others (e.g., database errors) specially.
Avoid infinite loops; enforce a maximum number of attempts to protect system stability.
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.
