feign实现方法级别注解超时配置

36 篇文章 2 订阅
3 篇文章 0 订阅

feign实现方法级别注解超时配置

测试的3.18新版本已经支持方法中参数带有Options 也可以自定义配置, Options options = findOptions(argv);;

使用该注解方式需配合AOP使用!

原理是包装自己的client客户端, 替换框架的客户端!
应用到生产环境需自己充验证测试

1.0 注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FeignTimeout {
    /**
     * 连接超时时间
     * @return
     */
    int connectTimeoutMillis() default 10000;

    /**
     * 读取超时时间
     * @return
     */
    int readTimeoutMillis() default 60000;
}

2.0 包装的客户端FeignTimeoutClient

import feign.Client;
import feign.Request;
import feign.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.Objects;

public class FeignTimeoutClient implements Client {

    private static final Logger log = LoggerFactory.getLogger(FeignTimeoutClient.class);

    private static final ThreadLocal<Request.Options> NOW_OPTIONS = new ThreadLocal<>();

    private final Client delegate;

    private FeignTimeoutClient(Client delegate){
        this.delegate = delegate;
    }

    public static Client wrap(Client client){
        return new FeignTimeoutClient(client);
    }

    public static void nowOptions(Request.Options options){
        NOW_OPTIONS.set(options);
    }

    public static void nowOptionsClear(){
        NOW_OPTIONS.remove();
    }

    @Override
    public Response execute(Request request, Request.Options options) throws IOException {
        // 如果未取到当前上下文的 自定义超时时间 则直接使用默认的,如果取到了则用特殊的
        Request.Options optionsNow = NOW_OPTIONS.get();
        if(Objects.nonNull(optionsNow)){
            log.info("feign命中自定义超时:url="+request.url()+";options.connectTimeoutMillis="+ optionsNow.connectTimeoutMillis()+",options.readTimeoutMillis="+optionsNow.readTimeoutMillis());
            return delegate.execute(request,optionsNow);
        }
        return delegate.execute(request,options);
    }
}

3.0 包装FeignContext

FeignTimeoutFeignContext上下文

import feign.Client;
import feign.Feign;
import lombok.SneakyThrows;
import org.apache.commons.collections4.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.openfeign.FeignContext;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class FeignTimeoutFeignContext extends FeignContext {
    private static final Logger log = LoggerFactory.getLogger(FeignTimeoutFeignContext.class);
    private final FeignContext delegate;

    private FeignTimeoutFeignContext(FeignContext delegate) {
        this.delegate = delegate;
    }

    @Override
    public <T> T getInstance(String name, Class<T> type) {
        T instance = delegate.getInstance(name, type);
        return (T) wrapIfShould(instance);
    }

    @Override
    public <T> Map<String, T> getInstances(String name, Class<T> type) {
        Map<String, T> instances = delegate.getInstances(name, type);
        if (MapUtils.isNotEmpty(instances)) {
            Map<String, T> convertedInstances = new HashMap<>();
            for (Map.Entry<String, T> entry : instances.entrySet()) {
                convertedInstances.put(entry.getKey(), (T) wrapIfShould(entry.getValue()));
            }
            return convertedInstances;
        }

        return instances;
    }

    @SneakyThrows
    private Object wrapIfShould(Object instance) {
        if (instance instanceof Feign.Builder) {
            Field field = instance.getClass().getDeclaredField("client");
            field.setAccessible(true);
            Client client = (Client) ReflectionUtils.getField(field, instance);
            if (client instanceof FeignTimeoutClient) {
                return instance;
            }
            Client wrap = FeignTimeoutClient.wrap(client);
            ReflectionUtils.setField(field, instance, wrap);
        }
        if (instance instanceof Client && !(instance instanceof FeignTimeoutClient)) {
            Object client = instance;
            return FeignTimeoutClient.wrap((Client) client);
        }
        return instance;
    }

    public static FeignTimeoutFeignContext wrap(FeignContext context) {
        return new FeignTimeoutFeignContext(context);
    }
}

bean后置处理器进行包装

package com.zhihao.fegin;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cloud.openfeign.FeignContext;
public class FeignTimeoutBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        // 对FeignContext进行包装
        if(bean instanceof FeignContext && !(bean instanceof FeignTimeoutFeignContext)){
            return FeignTimeoutFeignContext.wrap(((FeignContext) bean));
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

4.0 配置AOP注解拦截设置超时

package com.zhihao.fegin;
import feign.Request;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.core.annotation.AnnotationUtils;
import java.util.Objects;
public class FeignTimeoutMethodInterceptor implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        try {
            // 先查找当前方法以及方法所在类上的注解
            FeignTimeout feignTimeout = AnnotationUtils.findAnnotation(invocation.getMethod(), FeignTimeout.class);
            if(Objects.nonNull(feignTimeout)){
                FeignTimeoutClient.nowOptions(new Request.Options(feignTimeout.connectTimeoutMillis(), feignTimeout.readTimeoutMillis()));
            }
            return invocation.proceed();
        } finally {
            FeignTimeoutClient.nowOptionsClear();
        }
    }
}

5.0 使用配置类加入IOC容器

@Configuration
// 如果后续有配置需要在FeignClientsConfiguration初始化前使用则需要该注解, 配合@condition使用
// @AutoConfigureBefore(FeignClientsConfiguration.class)
public class FeignConfiguration {

    @Bean
    public FeignTimeoutBeanPostProcessor feignTimeoutBeanPostProcessor(){
        return new FeignTimeoutBeanPostProcessor();
    }

    @Bean
    public DefaultPointcutAdvisor feignTimeoutPointcutAdvisor(){
        FeignTimeoutMethodInterceptor interceptor = new FeignTimeoutMethodInterceptor();
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("@annotation(com.zhihao.spring.cloud.fegin.ext.timeout.FeignTimeout)");
        // 配置增强类advisor
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
        advisor.setPointcut(pointcut);
        advisor.setAdvice(interceptor);
        return advisor;
    }
}

6.0 使用

/**
 * @Author: ZhiHao
 * @Date: 2023/8/26 14:33
 * @Description:
 * @Versions 1.0
 **/
@FeignClient(name = "feignTimeOutTest",url = "https://test-zhiha.plus")
public interface FeignTimeOutTest {

    @GetMapping("/masterPriceManager/detail")
    @FeignTimeout(connectTimeoutMillis = 500,readTimeoutMillis = 300)
    Map<String, Object> detail(@RequestParam("masterId") Long masterId);
}

7.0 结果:

pPNDPOK.png

ail")
@FeignTimeout(connectTimeoutMillis = 500,readTimeoutMillis = 300)
Map<String, Object> detail(@RequestParam(“masterId”) Long masterId);
}


7.0 结果:

[[外链图片转存中...(img-YhTVhjsr-1693034378942)]](https://imgse.com/i/pPNDPOK)



1
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在使用 Feign 进行服务间通讯时,为了避免因服务调用超时导致的问题,需要对超时时间进行配置Feign 提供了两种方式进行超时时间配置,分别是全局配置和单个请求配置。 全局配置: 在 Feign配置文件中添加以下属性进行全局配置: ``` feign: client: config: default: connectTimeout: 5000 readTimeout: 5000 ``` 其中,`connectTimeout` 表示连接超时时间,`readTimeout` 表示读取超时时间,单位均为毫秒。 单个请求配置: 在定义 Feign 接口方法时,可以使用 `@FeignClient` 注解的 `configuration` 属性指定一个配置类,并在该配置类中进行单个请求的超时时间配置。示例代码如下: ``` @FeignClient(name = "example", configuration = ExampleFeignConfig.class) public interface ExampleFeignClient { @GetMapping("/example") @Headers("Content-Type: application/json") @RequestLine("GET /example") public String getExample(); } @Configuration public class ExampleFeignConfig { @Bean public Request.Options options() { return new Request.Options(5000, 5000); } } ``` 在上述示例中,`ExampleFeignConfig` 类中的 `options` 方法返回一个 `Request.Options` 对象,其中第一个参数表示连接超时时间,第二个参数表示读取超时时间,单位均为毫秒。 需要注意的是,单个请求配置会覆盖全局配置。所以如果在全局配置中已经指定了超时时间,而在单个请求配置中又指定了不同的超时时间,那么实际生效的是单个请求配置中指定的超时时间

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

懵懵懂懂程序员

如果节省了你的时间, 请鼓励

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值