Принудительная повторная попытка для определенного кода состояния http

Имея дело с конкретным веб-сайтом, иногда я получаю http-ответ с кодом состояния 403. В таких случаях я хотел повторно выполнить запрос (потому что в моей конкретной ситуации этот сервер выдает 403, когда он фактически перегружен). Я пытался использовать ResponseHandler вместе с StandardHttpRequestRetryHandler, но это не сработало так, как я надеялся; Я ожидал, что исключение в ResponseHandler вызовет StandardHttpRequestRetryHandler, но, похоже, это не так. Как я могу добиться желаемой функциональности?

Вот пример кода, который иллюстрирует мою ситуацию:

import java.io.IOException;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.StandardHttpRequestRetryHandler;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;


public class Main {

  public static void main(String[] args) throws Exception {

    // a response handler that throws an exception if status is not 200
    ResponseHandler<String> responseHandler = new ResponseHandler<String> () {

      @Override
      public String handleResponse(HttpResponse response) throws
        ClientProtocolException, IOException
      {
        System.out.println("-> Handling response");

        if (response.getStatusLine().getStatusCode() != 200){
          // I expected this to trigger the retryHandler 
          throw new ClientProtocolException("Status code not supported");
        }
        return EntityUtils.toString(response.getEntity());
      }

    };

    StandardHttpRequestRetryHandler retryHandler = 
        new StandardHttpRequestRetryHandler(5, true)
    {
      @Override
      public boolean retryRequest(
          final IOException exception,
          final int executionCount,
          final HttpContext context)
      {
        System.out.println("-> Retrying request");
        return super.retryRequest(exception, executionCount, context);
      }
    };

    // my client with my retry handler
    HttpClient client = HttpClients
        .custom()
        .setRetryHandler(retryHandler)
        .build();

    // my request
    HttpUriRequest request = RequestBuilder
        .create("GET")
        .setUri("http://httpstat.us/403")         //always returns 403
        .build();

    String contents = client.execute(request, responseHandler);

    System.out.println(contents);
  }


}

person Cacovsky    schedule 03.04.2014    source источник
comment
я думаю, вы имеете в виду 503 - служба недоступна   -  person emicklei    schedule 08.04.2016


Ответы (2)


Попробуйте использовать пользовательское ServiceUnavailableRetryStrategy

CloseableHttpClient client = HttpClients.custom()
        .setServiceUnavailableRetryStrategy(new ServiceUnavailableRetryStrategy() {
            @Override
            public boolean retryRequest(
                    final HttpResponse response, final int executionCount, final HttpContext context) {
                int statusCode = response.getStatusLine().getStatusCode();
                return statusCode == 403 && executionCount < 5;
            }

            @Override
            public long getRetryInterval() {
                return 0;
            }
        })
        .build();
person ok2c    schedule 08.04.2014
comment
Это именно то, что мы придумали. Я только что опубликовал этот же самый ответ, спасибо! - person Cacovsky; 08.04.2014
comment
Это отлично подходит для случая, когда служба 503 недоступна, в ней даже есть слово «недоступно» прямо в названии. К сожалению, я пришел сюда в поисках ответа на вопрос заголовка, а именно: как повторить запрос для данного кода состояния или другого условия. У меня есть вариант использования, когда мне нужно повторно аутентифицироваться при получении 401 и повторить запрос, и хотя я мог бы переопределить ServiceUnavailableRetryStrategy, это действительно похоже на взлом, учитывая, что интерфейс, похоже, предназначен специально для случаев сервера быть недоступным. Если бы они назвали это как-то иначе - person one stevy boi; 15.10.2019
comment
@one stevy boi - согласен с вами - застрял на одном и том же месте, интересно, вы нашли лучшее решение? - person kjkszpj; 03.05.2021

Вы можете сделать это вручную, проверив код состояния, примерно так:

CloseableHttpResponse response = null;
boolean success = false;
while(!success) {
    response = client.execute(httpGet);
    int status = response.getStatusLine().getStatusCode();
    success = (status == 200);
    if (!success) {
        if(status == 403) {
            Thread.sleep(2000); // wait 2 seconds before retrying
        } else {
            throw new RuntimeException("Something went wrong: HTTP status: " + status);
        }
    }
}
String contents = EntityUtils.toString(response.getEntity());
response.close();

// ....

System.out.println(contents);

Вам нужно будет добавить некоторые вещи, такие как повторные попытки предопределенное количество раз, прежде чем генерировать окончательное исключение и перехватывать некоторые проверенные исключения (например, InterruptedException, генерируемое Thread.sleep()), но в основном код показывает основную идею.

person morgano    schedule 03.04.2014
comment
И не забудьте добавить какое-то ограничение на количество повторных попыток, на тот случай, если 403 действительно означает «запрещено». В любом случае, если есть возможность, почините сломанный сервер. - person Drunix; 03.04.2014
comment
Спасибо за ваш ответ. На самом деле, поскольку есть RetryHandler, я ожидаю какой-то интеграции между RetryHandler и ResponseHandler, избегая предложенного вами решения. Но кажется, что использовать подключаемые компоненты HttpComponents невозможно. Тем не менее, я буду продолжать попытки. - person Cacovsky; 03.04.2014
comment
Пожалуйста, посмотрите ответ Олега, это было именно то, что я искал. - person Cacovsky; 08.04.2014