Mastering API Retry Strategies: 8 Proven Methods for Reliable Backend Calls
This article explores eight practical retry mechanisms for handling unreliable third‑party APIs in backend systems, covering simple loops, recursion, built‑in client retries, Spring Retry, Resilience4j, custom utilities, asynchronous thread‑pool retries, and message‑queue based approaches, plus best‑practice tips.
In cross‑border services, third‑party servers are distributed worldwide, so network issues when calling external APIs are common; a retry mechanism becomes essential.
Retry Mechanism Implementation
1. Loop Retry
This is the simplest method: wrap the request in a loop and retry until success or the maximum attempts are reached.
Example code:
int retryTimes = 3;<br/>for(int i = 0; i < retryTimes; i++) {<br/> try {<br/> // request code<br/> break;<br/> } catch(Exception e) {<br/> // handle exception<br/> Thread.sleep(1000); // delay 1 second before retry<br/> }<br/>}<br/>The loop uses a for construct with a maximum of three attempts, and Thread.sleep() adds a delay to avoid rapid retries.
2. Recursive Retry
Recursion can also be used: the method calls itself on failure until success or the retry limit.
Example code:
public void requestWithRetry(int retryTimes) {<br/> if(retryTimes <= 0) return;<br/> try {<br/> // request code<br/> } catch(Exception e) {<br/> Thread.sleep(1000); // delay 1 second<br/> requestWithRetry(retryTimes - 1);<br/> }<br/>}<br/>3. Built‑in Client Retry
Many HTTP clients have built‑in retry support. For Apache HttpClient:
Version 4.5+:
HttpClients.custom().setRetryHandler(...) CloseableHttpClient httpClient = HttpClients.custom()<br/> .setRetryHandler(new DefaultHttpRequestRetryHandler(3, true))<br/> .build();<br/>Version 5.x:
HttpClients.custom().setRetryStrategy(...) CloseableHttpClient httpClient = HttpClients.custom()<br/> .setRetryStrategy(new DefaultHttpRequestRetryStrategy(3, NEG_ONE_SECOND))<br/> .build();<br/>Custom retry strategies can be implemented by providing HttpRequestRetryHandler (4.5+) or RetryStrategy (5.x).
4. Spring Retry Library
Spring Retry offers annotations and utilities for retrying methods.
<dependency><br/> <groupId>org.springframework.retry</groupId><br/> <artifactId>spring-retry</artifactId><br/> <version>1.3.1</version><br/></dependency><br/>Explicit RetryTemplate
Create a RetryTemplate and configure policies.
RetryTemplate retryTemplate = new RetryTemplate();<br/><br/>// configure retry policy<br/>RetryPolicy retryPolicy = new SimpleRetryPolicy(3);<br/>retryTemplate.setRetryPolicy(retryPolicy);<br/><br/>// configure back‑off policy<br/>FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();<br/>backOffPolicy.setBackOffPeriod(1000);<br/>retryTemplate.setBackOffPolicy(backOffPolicy);<br/>Execute the method via retryTemplate.execute(...).
retryTemplate.execute((RetryCallback<Void, Exception>) context -> {<br/> // request code<br/> return null;<br/>});<br/>Alternatively, use @Retryable on methods:
@Retryable(value = Exception.class, maxAttempts = 3)<br/>public void request() {<br/> // request code<br/>}<br/>Annotation‑Based Retry
Enable retry with @EnableRetry and annotate methods with @Retryable, optionally configuring back‑off with @Backoff.
@Configuration<br/>@EnableRetry<br/>public class RetryConfig { }<br/><br/>@Service<br/>public class MyService {<br/> @Retryable(value = MyException.class, maxAttempts = 3, backoff = @Backoff(delay = 1000))<br/> public void doSomething() {<br/> // request logic<br/> }<br/>}<br/>5. Resilience4j
Resilience4j provides lightweight retry, circuit‑breaker, and rate‑limiter features.
<dependency><br/> <groupId>io.github.resilience4j</groupId><br/> <artifactId>resilience4j-spring-boot2</artifactId><br/> <version>1.7.0</version><br/></dependency><br/>Programmatic Use
Create a RetryRegistry.
RetryRegistry retryRegistry = RetryRegistry.ofDefaults();<br/>Configure a Retry instance.
RetryConfig config = RetryConfig.custom()<br/> .maxAttempts(3)<br/> .waitDuration(Duration.ofMillis(1000))<br/> .retryOnResult(response -> response.getStatus() == 500)<br/> .retryOnException(e -> e instanceof WebServiceException)<br/> .retryExceptions(IOException.class, TimeoutException.class)<br/> .ignoreExceptions(BusinessException.class, OtherBusinessException.class)<br/> .failAfterMaxAttempts(true)<br/> .build();<br/><br/>Retry retry = retryRegistry.retry("name", config);<br/>Execute code with Retry.decorateCheckedSupplier.
CheckedFunction0<String> retryableSupplier = Retry.decorateCheckedSupplier(retry, () -> {<br/> // request code<br/> return "result";<br/>});<br/>Annotation Use
@Service<br/>public class MyService {<br/> @Retryable(value = MyException.class, maxAttempts = 3, backoff = @Backoff(delay = 1000))<br/> public void doSomething() {<br/> // request logic<br/> }<br/>}<br/>6. Custom Retry Utility
A lightweight custom retry framework can be built with a Callback abstract class, a RetryResult holder, and a RetryExecutor that loops until success or the retry limit.
public abstract class Callback {<br/> public abstract RetryResult doProcess();<br/>}<br/><br/>public class RetryResult {<br/> private Boolean isRetry;<br/> private Object obj;<br/> // constructors, getters omitted<br/> public static RetryResult ofResult(Boolean isRetry, Object obj) {<br/> return new RetryResult(isRetry, obj);<br/> }<br/> public static RetryResult ofResult(Boolean isRetry) {<br/> return new RetryResult(isRetry, null);<br/> }<br/>}<br/><br/>public class RetryExecutor {<br/> public static Object execute(int retryCount, Callback callback) {<br/> for(int cur = 0; cur < retryCount; cur++) {<br/> RetryResult result = callback.doProcess();<br/> if(result.isRetry()) continue;<br/> return result.getObj();<br/> }<br/> return null;<br/> }<br/>}<br/>Usage example:
int maxRetryCount = 3;<br/>Object result = RetryExecutor.execute(maxRetryCount, new Callback() {<br/> @Override<br/> public RetryResult doProcess() {<br/> // request logic<br/> // return RetryResult.ofResult(true) to retry<br/> // or RetryResult.ofResult(false, result) to finish<br/> }<br/>});<br/>7. Asynchronous Thread‑Pool Retry
Use a ThreadPoolExecutor to submit request tasks and retry asynchronously.
int maxRetryTimes = 3;<br/>int currentRetryTimes = 0;<br/><br/>ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());<br/><br/>Callable<String> task = () -> {<br/> // request code<br/> return "result";<br/>};<br/><br/>Future<String> future;<br/>while(currentRetryTimes < maxRetryTimes) {<br/> try {<br/> future = executor.submit(task);<br/> String result = future.get();<br/> break;<br/> } catch(Exception e) {<br/> currentRetryTimes++;<br/> try { Thread.sleep(1000); } catch(InterruptedException ex) { Thread.currentThread().interrupt(); }<br/> }<br/>}<br/>8. Message‑Queue Retry
Persist failed requests to a message queue (e.g., RocketMQ) to ensure reliability.
@Component<br/>@RocketMQMessageListener(topic = "myTopic", consumerGroup = "myConsumerGroup")<br/>public class MyConsumer implements RocketMQListener<String> {<br/> @Override<br/> public void onMessage(String message) {<br/> try {<br/> // request code<br/> } catch(Exception e) {<br/> DefaultMQProducer producer = new DefaultMQProducer("myProducerGroup");<br/> producer.setNamesrvAddr("127.0.0.1:9876");<br/> try {<br/> producer.start();<br/> Message msg = new Message("myTopic", "myTag", message.getBytes());<br/> producer.send(msg);<br/> } catch(Exception ex) {<br/> // handle send error<br/> } finally {<br/> producer.shutdown();<br/> }<br/> }<br/> }<br/>}<br/>Best Practices and Precautions
Set reasonable retry counts and intervals to avoid overwhelming the system.
Consider idempotency; avoid duplicate writes on non‑idempotent APIs.
Handle concurrency with locks or distributed locks to prevent duplicate requests.
Differentiate exception types: retry on transient network errors, handle permanent errors separately.
Avoid infinite loops; enforce maximum attempts 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.
