This article is based on HttpClient 4.5.13

When requesting external services using HTTP, retries are often required due to network or service instability. Retry can certainly be achieved by hand-lifting code, but a better way is to achieve it through existing mechanisms. Two types of retries are supported in HttpClient:

  1. Retry the exception.
  2. Service unavailable retry.

Abnormal retry

HttpClient throws two types of exceptions:

  • java.io.IOException
  • ClientProtocolException

Java.io.IOException is considered nonfatal and recoverable, while ClientProtocolException is considered fatal and unrecoverable.

Note that ClientProtocolException is a subclass of java.io.IOException.

If this is how you create HttpClient

CloseableHttpClient httpClient = HttpClients.custom().build();
Copy the code

Exception retry is enabled by default. See the httpClientBuilder.build () method for the code below

// Add request retry executor, if not disabled
if(! automaticRetriesDisabled) { HttpRequestRetryHandler retryHandlerCopy =this.retryHandler;
    if (retryHandlerCopy == null) {
        retryHandlerCopy = DefaultHttpRequestRetryHandler.INSTANCE;
    }
    execChain = new RetryExec(execChain, retryHandlerCopy);
}
Copy the code

AutomaticRetriesDisabled isa Boolean variable that defaults to false, so the condition defaults to HttpRequestRetryHandler using a default if HttpRequestRetryHandler is not set.

DefaultHttpRequestRetryHandler mainly has three member variables

  • retryCountRetry count
  • requestSentRetryEnabledWhether the request can be retried after it was successfully sent, but not after the request was successfully sent.
  • nonRetriableClassesA collection of non-retry exception classes that will not be retried if the exception is one specified in the collection.

The default instance variable Settings are as follows

  • retryCount=3To retry a maximum of three times.
  • requestSentRetryEnabled=falseIf the message is successfully sent, it will not retry
  • nonRetriableClassesIt contains the following four types:
    • InterruptedIOException
    • UnknownHostException
    • ConnectException
    • SSLException

Retry the execution of the logic in the org. Apache. HTTP. Impl. Execchain. RetryExec, interested can go to the next.

The default retry logic is as follows

@Override
public boolean retryRequest(final IOException exception, final int executionCount, final HttpContext context) {
    Args.notNull(exception, "Exception parameter");
    Args.notNull(context, "HTTP context");
    if (executionCount > this.retryCount) {
        // Do not retry if over max retry count
        // The number of retries is exceeded
        return false;
    }
    // Do not retry if the exception is ignored
    if (this.nonRetriableClasses.contains(exception.getClass())) {
        return false;
    }
    for (final Class<? extends IOException> rejectException : this.nonRetriableClasses) {
        if (rejectException.isInstance(exception)) {
            return false; }}final HttpClientContext clientContext = HttpClientContext.adapt(context);
    final HttpRequest request = clientContext.getRequest();

    if(requestIsAborted(request)){
        return false;
    }
    // Idempotent methods can be retried
    if (handleAsIdempotent(request)) {
        // Retry if the request is considered idempotent
        return true;
    }
    // Try again if the request is not sent or if it is
    if(! clientContext.isRequestSent() ||this.requestSentRetryEnabled) {
        // Retry if the request has not been sent fully or
        // if it's OK to retry methods that have been sent
        return true;
    }
    // otherwise do not retry
    return false;
}
Copy the code

Note that the lower idempotent methods are not post or PUT, so the judgment will not hold, but if requestSentRetryEnabled is set to True, retransmissions will still occur, and you need to ensure that the interface being called is idempotent when reprocessing POST and PUT requests.

Some people may experience problems, such as reporting an exception for SocketTimeoutException but not retrying it, because SocketTimeoutException is a subclass of InterruptedIOException and is ignored by default. If you need to retry, you can customize an HttpRequestRetryHandler and then set it up again.

HttpClients.custom().setRetryHandler(httpRequestRetryHandler).build();
Copy the code

Actually use inheritance DefaultHttpRequestRetryHandler, then extend some own implementation is very convenient.

It’s also easy to disable callback retries if you want

HttpClients.custom().disableAutomaticRetries().build();
Copy the code

Service unavailable retry

In some cases, the request was successful, but the HTTP status code may not be 2xx, in which case you need to retry. HttpClient provides a mechanism for retrying when a service is unavailable.

Retry logic of execution in the org. Apache. HTTP. Impl. Execchain. ServiceUnavailableRetryExec, interested can look at.

The HttpClient provides the default policy, but it is not enabled by default

DefaultServiceUnavailableRetryStrategy serviceUnavailableRetryStrategy = new DefaultServiceUnavailableRetryStrategy();
httpClient = HttpClients.custom().setServiceUnavailableRetryStrategy(serviceUnavailableRetryStrategy).build();
Copy the code

Retry logic

@Override
public boolean retryRequest(final HttpResponse response, final int executionCount, final HttpContext context) {
    return executionCount <= maxRetries &&
        response.getStatusLine().getStatusCode() == HttpStatus.SC_SERVICE_UNAVAILABLE;
}
Copy the code

When there is no more than retries and retry return code is 503, of course, can also custom ServiceUnavailableRetryStrategy to achieve your needs.

It is also possible to set the interval between retry requests.