Java › Java Networking & HTTP Client

How would you implement retry logic with exponential backoff for HTTP requests made via HttpClient, given the client doesn't provide built-in retry support natively?

Advanced

Answer

Since HttpClient doesn't include built-in retry logic, implementing resilient retry behavior requires wrapping request execution in your own retry loop (or using a dedicated resilience library like Resilience4j or Failsafe), catching transient failures (specific IOException subtypes, or specific HTTP status codes like 503 Service Unavailable) and retrying with an exponentially increasing delay between attempts (doubling the wait time after each failed attempt, often with added random jitter to avoid many clients retrying in lockstep and overwhelming a recovering server simultaneously), up to a maximum number of attempts before ultimately giving up and propagating the failure.
public <T> HttpResponse<T> sendWithRetry(HttpRequest request, HttpResponse.BodyHandler<T> handler, int maxAttempts) throws Exception {
    int attempt = 0;
    while (true) {
        try {
            HttpResponse<T> response = client.send(request, handler);
            if (response.statusCode() < 500) return response;  // don't retry client errors, only server errors
            throw new IOException("Server error: " + response.statusCode());
        } catch (IOException e) {
            attempt++;
            if (attempt >= maxAttempts) throw e;
            long delayMs = (long) (Math.pow(2, attempt) * 100);  // exponential backoff
            Thread.sleep(delayMs);
        }
    }
}
Real-world example A payment processing client retries a failed downstream API call up to three times with exponential backoff (200ms, then 400ms, then 800ms delays) specifically for 5xx server errors and network-level IOExceptions, while deliberately NOT retrying 4xx client errors (like an invalid request), since retrying a genuinely malformed request would just fail identically every time.

Common follow-ups: Why is it important to add random jitter to exponential backoff delays in a system with many concurrent clients, rather than using purely deterministic delay values?;What HTTP status codes and exception types genuinely warrant a retry versus those that don't?