Java 强制重试特定的 http 状态代码

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22827291/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 18:09:07  来源:igfitidea点击:

Force retry on specific http status code

javaapache-httpclient-4.xapache-httpcomponents

提问by Cacovsky

Dealing with a specific website, sometimes I receive a http response with status code 403. I wanted to re-execute the request in such cases (because, in my specific situation, this server throws a 403 when it is actually overloaded). I tried to use a ResponseHandlertogether with a StandardHttpRequestRetryHandler, but it didn't work the way I hoped; I expected that throwing an exception in the ResponseHandlerwould trigger the StandardHttpRequestRetryHandler, but it does not seems to be the case. How can I achieve the desired functionality?

处理特定网站时,有时我会收到状态码为 403 的 http 响应。我想在这种情况下重新执行请求(因为,在我的特定情况下,该服务器在实际过载时会抛出 403)。我尝试将 aResponseHandler与a一起使用StandardHttpRequestRetryHandler,但没有按照我希望的方式工作;我预计在 中抛出异常ResponseHandler会触发StandardHttpRequestRetryHandler,但似乎并非如此。我怎样才能实现所需的功能?

Here is a sample code that illustrates my situation:

这是一个示例代码,说明了我的情况:

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);
  }


}

采纳答案by ok2c

Try using a custom ServiceUnavailableRetryStrategy

尝试使用自定义 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();

回答by morgano

You can do it by manually checking the status code, something like this:

您可以通过手动检查状态代码来完成,如下所示:

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);

You would need to add some things like retrying a predefined number of times before throwing a final exception and catching some checked exceptions (like the InterruptedException thrown by Thread.sleep()), but basically the code shows the main idea.

您需要添加一些内容,例如在抛出最终异常之前重试预定义的次数并捕获一些已检查的异常(如 抛出的 InterruptedException Thread.sleep()),但基本上代码显示了主要思想。