SpringCloudOpenFeign奇淫技巧
feign之requestInterceptor使用
requestInterceptor 一般使用场景:添加token、Authorization、微服务上下游传递参数等,我们就来一个简单的Basic认证
Basic认证requestInterceptor
- 实现requestInterceptor类
- 配置文件添加
feign.config.default.requestInterceptors: - service.consumer.BasicAuthRequestInterceptor
- 代码如下:
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.util.Base64Utils;
import java.nio.charset.StandardCharsets;
public class BasicAuthRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
template.header("Authorization", "Basic " +
Base64Utils.encodeToString("user:admin".getBytes(StandardCharsets.UTF_8)));
}
}
feign之errorDecoder 使用
ErrorDecoder 主要用处理httpstauts 为4XX,5XX的处理,使用场景:feign调用将会抛出FeignException. 由于其异常message经过了Feint的封装, 所以不再是服务提供方的原始异常信息. 若想展示原始信息则需要重写ErrorDecoder来实现
import feign.FeignException;
import feign.Response;
import feign.RetryableException;
import feign.codec.ErrorDecoder;
import java.util.Date;
import static feign.FeignException.errorStatus;
public class RetryableErrorDecoder implements ErrorDecoder {
@Override
public Exception decode(String methodKey, Response response) {
FeignException exception = errorStatus(methodKey, response);
return new RetryableException(
response.status(),
exception.getMessage(),
response.request().httpMethod(),
exception,
new Date(),
response.request());
}
}
feign之retryer使用
retryer主要用于feign client 重试功能,主要针对IOException异常进行重试,feign 默认配置为NEVER_RETRY,我们也可以通过实现Retryer ,可以参考feign.Retryer.Default
- retryer 类代码如下:
/**
* Cloned for each invocation to {@link Client#execute(Request, feign.Request.Options)}.
* Implementations may keep state to determine if retry operations should continue or not.
*/
public interface Retryer extends Cloneable {
/**
* if retry is permitted, return (possibly after sleeping). Otherwise propagate the exception.
*/
void continueOrPropagate(RetryableException e);
Retryer clone();
class Default implements Retryer {
private final int maxAttempts;
private final long period;
private final long maxPeriod;
int attempt;
long sleptForMillis;
public Default() {
this(100, SECONDS.toMillis(1), 5);
}
public Default(long period, long maxPeriod, int maxAttempts) {
this.period = period;
this.maxPeriod = maxPeriod;
this.maxAttempts = maxAttempts;
this.attempt = 1;
}
// visible for testing;
protected long currentTimeMillis() {
return System.currentTimeMillis();
}
public void continueOrPropagate(RetryableException e) {
if (attempt++ >= maxAttempts) {
throw e;
}
long interval;
if (e.retryAfter() != null) {
interval = e.retryAfter().getTime() - currentTimeMillis();
if (interval > maxPeriod) {
interval = maxPeriod;
}
if (interval < 0) {
return;
}
} else {
interval = nextMaxInterval();
}
try {
Thread.sleep(interval);
} catch (InterruptedException ignored) {
Thread.currentThread().interrupt();
throw e;
}
sleptForMillis += interval;
}
/**
* Calculates the time interval to a retry attempt. <br>
* The interval increases exponentially with each attempt, at a rate of nextInterval *= 1.5
* (where 1.5 is the backoff factor), to the maximum interval.
*
* @return time in nanoseconds from now until the next attempt.
*/
long nextMaxInterval() {
long interval = (long) (period * Math.pow(1.5, attempt - 1));
return interval > maxPeriod ? maxPeriod : interval;
}
@Override
public Retryer clone() {
return new Default(period, maxPeriod, maxAttempts);
}
}
/**
* Implementation that never retries request. It propagates the RetryableException.
*/
Retryer NEVER_RETRY = new Retryer() {
@Override
public void continueOrPropagate(RetryableException e) {
throw e;
}
@Override
public Retryer clone() {
return this;
}
};
}
- feign 实现重试关键代码:
SynchronousMethodHandler.invoke方法
@Override
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = buildTemplateFromArgs.create(argv);
Options options = findOptions(argv);
Retryer retryer = this.retryer.clone();
while (true) {
try {
//执行请求并解析响应参数
return executeAndDecode(template, options);
} //如果出现RetryableException就调用continueOrPropagate方法进行重试
catch (RetryableException e) {
try {
retryer.continueOrPropagate(e);
} catch (RetryableException th) {
Throwable cause = th.getCause();
if (propagationPolicy == UNWRAP && cause != null) {
throw cause;
} else {
throw th;
}
}
if (logLevel != Logger.Level.NONE) {
logger.logRetry(metadata.configKey(), logLevel);
}
continue;
}
}
}