<aside> 👉🏼 init
</aside>
@Configuration
注解, 不让spring扫描到
@FeignClient
注解加configuration =XXXClientConfig.class
参数写一个空的RequestInterceptor
然后和全局的拦截器BeanName一样就行了
feign如何在全局不开debug的情况下打印log
@Bean
public Logger feignLogger() {
return new Logger() {
private final org.slf4j.Logger logger = LoggerFactory.getLogger(Slf4jLogger.class);
@Override
protected void log(String configKey, String format, Object... args) {
logger.info(String.format(methodTag(configKey) + format, args));
}
};
}
在application.yml
中配置Feign Logger
feign:
client:
config:
exampleClient: # 对特定客户端配置日志
loggerLevel: FULL
*BASIC
日志*
*HEADERS*
日志
*FULL*
日志 多一个返回
@RequiredArgsConstructor
public class XXXClientConfig {
/**
* 给XXX的feign请求添加token
* 和全局拦截器feignInterceptorConfiguration同名, 这个会覆盖全局拦截器
*
* @return
*/
@Bean
public RequestInterceptor feignInterceptorConfiguration() {
return requestTemplate -> {
String token = xxx.getToken("1");
requestTemplate.header("Authorization", "Bearer " + token);
};
}
private final ErrorDecoder defaultErrorDecoder = new ErrorDecoder.Default();
@Bean
public ErrorDecoder errorDecoder() {
return new ErrorDecoder() {
@Override
public Exception decode(String methodKey, Response response) {
if (response.status() == HttpStatus.UNAUTHORIZED.value()) {
// 抛出一个RetryableException,Feign会自动重试请求
return new RetryableException(response.status(),
"Token expired, retrying request with new token",
response.request().httpMethod(), null, response.request());
}
return defaultErrorDecoder.decode(methodKey, response);
}
};
private final ErrorDecoder defaultErrorDecoder = new ErrorDecoder.Default();
@Bean
public ErrorDecoder errorDecoder() {
return (methodKey, response) -> {
if (response.status() == HttpStatus.UNAUTHORIZED.value()) {
// 抛出一个RetryableException,Feign会自动重试请求
return new MyRetryException(response.status(),
"Token expired",
response.request().httpMethod(), null, response.request());
}
return defaultErrorDecoder.decode(methodKey, response);
};
}
static class MyRetryException extends RetryableException {
public MyRetryException(int status, String message, Request.HttpMethod httpMethod, Date cause, Request request) {
super(status, message, httpMethod, cause, request);
}
}
@Bean
public Retryer retryer() {
return new MyDefault();
}
private class MyDefault extends Retryer.Default {
public MyDefault() {
super(1000, 1000, 4);
}
@Override
public void continueOrPropagate(RetryableException e) {
if (e instanceof MyRetryException && e.getClass().equals(MyRetryException.class)) {
modelHouseAuthRepository.refreshToken();
super.continueOrPropagate(e);
return;
}
throw e;
}
@Override
public Retryer clone() {
return new MyDefault();
}
}
}
这一行代码的主要目的是解析HTTP响应头中的Retry-After
字段,以确定何时应该进行重试。这在处理服务端返回的429 Too Many Requests
或503 Service Unavailable
响应时特别有用,因为这些响应通常会包含一个Retry-After
头来指示客户端何时可以再次尝试请求。
Date retryAfter = retryAfterDecoder.apply(firstOrNull(response.headers(), RETRY_AFTER));
response.headers()
: 获取HTTP响应的所有头信息。RETRY_AFTER
: 常量字符串,通常为"Retry-After"
,表示HTTP响应头中的Retry-After
字段。firstOrNull(response.headers(), RETRY_AFTER)
: 从响应头中获取Retry-After
字段的值。如果存在多个值,通常只取第一个值;如果没有这个字段,则返回null
。retryAfterDecoder.apply(...)
: 将Retry-After
字段的值传递给retryAfterDecoder
进行解析。retryAfterDecoder
通常是一个函数或方法,用于将Retry-After
字段的值转换为一个Date
对象。Retry-After
头的作用Retry-After
头通常用于告诉客户端在多长时间后可以重新尝试请求。它的值可以是以下两种形式之一:
Wed, 21 Oct 2015 07:28:00 GMT
。120
(表示120秒后重试)。retryAfterDecoder
的实现retryAfterDecoder
需要能够处理这两种形式,并将它们转换为一个Date
对象。以下是一个简单的实现示例:
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.text.ParseException;
import java.text.SimpleDateFormat;
public class RetryAfterDecoder {
private static final SimpleDateFormat HTTP_DATE_FORMAT =
new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss zzz");
public Date apply(String retryAfterHeader) {
if (retryAfterHeader == null) {
return null;
}
try {
// 尝试解析为HTTP日期
return HTTP_DATE_FORMAT.parse(retryAfterHeader);
} catch (ParseException e) {
// 如果解析失败,尝试解析为秒数
try {
long seconds = Long.parseLong(retryAfterHeader);
return new Date(System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(seconds));
} catch (NumberFormatException nfe) {
// 无法解析为日期或秒数,返回null
return null;
}
}
}
}