seaswalker / posts

0 stars 0 forks source link

Feign什么时候会重试 #15

Open seaswalker opened 3 years ago

seaswalker commented 3 years ago

出现IOException

SynchronousMethodHandler的executeAndDecode方法:

Object executeAndDecode(RequestTemplate template) throws Throwable {
    Request request = targetRequest(template);
    Response response;
    long start = System.nanoTime();
    try {
        response = client.execute(request, options);
    } catch (IOException e) {
        throw errorExecuting(request, e);
    }
}

errorExecuting方法抛出了RetryableException:

static FeignException errorExecuting(Request request, IOException cause) {
    return new RetryableException(
        format("%s executing %s %s", cause.getMessage(), request.httpMethod(), request.url()),
        request.httpMethod(),
        cause,
        null);
}

而feign正是通过判断catch到RetryableException来进行重试,invoke方法:

@Override
public Object invoke(Object[] argv) throws Throwable {
    RequestTemplate template = buildTemplateFromArgs.create(argv);
    Retryer retryer = this.retryer.clone();
    while (true) {
        try {
            return executeAndDecode(template);
        } catch (RetryableException e) {
            try {
                retryer.continueOrPropagate(e);
            } catch (RetryableException th) {
                Throwable cause = th.getCause();
                if (propagationPolicy == UNWRAP && cause != null) {
                    throw cause;
                } else {
                    throw th;
                }
        }
            continue;
        }
    }
}

接口调用时常见的异常: ConnectException(连接超时)、SocketTimeoutException(读超时),都是IOException的子类,而且写入超时更是直接抛出IOException.

429, HttpTooManyRequests

还是SynchronousMethodHandler的executeAndDecode:

Object executeAndDecode(RequestTemplate template) throws Throwable {
    Request request = targetRequest(template);
    Response response;
    response = client.execute(request, options);
    boolean shouldClose = true;
    if (response.status() >= 200 && response.status() < 300) {
        // ...
    } else if (decode404 && response.status() == 404 && void.class != metadata.returnType()) {
        // ...
    } else {
        // 入口在这里
        throw errorDecoder.decode(metadata.configKey(), response);
    }
}

feign.codec.Default.decode:

@Override
public Exception decode(String methodKey, Response response) {
    FeignException exception = errorStatus(methodKey, response);
    Date retryAfter = retryAfterDecoder.apply(firstOrNull(response.headers(), RETRY_AFTER));
    if (retryAfter != null) {
        return new RetryableException(
            exception.getMessage(),
            response.request().httpMethod(),
            exception,
            retryAfter);
        }
    return exception;
}

所以,当响应码是429,并且存在retry-after响应头时才会进行重试,当然重试的前提是配置了feign的Retryer. 这里使用的feign版本是10.1.0.