<aside> 👉🏼 init

</aside>

Link


Feign 使用

1. 某个Client单独配置

  1. Config类不写

@Configuration 注解, 不让spring扫描到

  1. @FeignClient 注解加configuration =XXXClientConfig.class 参数
  2. 如果需要覆盖全局拦截器, BeanName需要和全局拦截器一样

某个Clinet不走全局拦截器

写一个空的RequestInterceptor

然后和全局的拦截器BeanName一样就行了

2. Retry

  1. 定义ErrorDecoder
  2. 配置Retryer 注意Retryer一定要重写 clone()

3. 日志

feign如何在全局不开debug的情况下打印log

  1. 自定义Feign Logger
    @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));

            }
        };
    }
  1. application.yml中配置Feign Logger feign: client: config: exampleClient: # 对特定客户端配置日志 loggerLevel: FULL

  2. *BASIC 日志*

    Untitled

  3. *HEADERS*日志

    Untitled

  4. *FULL*日志 多一个返回

    Untitled

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

4. Retry-After 有什么用

这一行代码的主要目的是解析HTTP响应头中的Retry-After字段,以确定何时应该进行重试。这在处理服务端返回的429 Too Many Requests503 Service Unavailable响应时特别有用,因为这些响应通常会包含一个Retry-After头来指示客户端何时可以再次尝试请求。

代码解析

Date retryAfter = retryAfterDecoder.apply(firstOrNull(response.headers(), RETRY_AFTER));

Retry-After 头的作用

Retry-After头通常用于告诉客户端在多长时间后可以重新尝试请求。它的值可以是以下两种形式之一:

  1. HTTP日期: 例如,Wed, 21 Oct 2015 07:28:00 GMT
  2. 以秒为单位的延迟时间: 例如,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;
            }
        }
    }
}