Feign拦截器熔断机制踩坑?

记一次fiegn拦截器传递token的一个坑,发生甚么事了?

事情是这样的,今天调试调用一个微服务的项目,但是发现在调用的过程中发现内部调用服务的时候 token 没有传递过去。

先看下yaml的配置:

feign:
  hystrix:
    enabled: true # 开启熔断
  client:
    config:
      default:
        connectTimeout: 100000
        readTimeout: 100000
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 30000
ribbon:
  ReadTimeout: 60000
  ConnectTimeout: 60000

关键在于开启熔断,此时feign接口熔断机制为:线程模式,熔断机制正常

使用RequestInterceptor拦截器

@Configuration
public class FeignConfiguration implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate requestTemplate) {
        ServletRequestAttributes attributes = (ServletRequestAttributes)
                RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        Enumeration<String> headerNames = request.getHeaderNames();
        if (headerNames != null) {
            while (headerNames.hasMoreElements()) {
                String name = headerNames.nextElement();
                String values = request.getHeader(name);
                requestTemplate.header(name, values);
            }
        }
    }
    @Bean
    public Logger.Level config(){
        return Logger.Level.FULL;
    }
}

拦截没有啥问题,但是报错了,如下:

Feign拦截器熔断机制踩坑?Feign拦截器熔断机制踩坑?

啪的一下很快就报错了哈,我都没反应过来

出现该错误原因:

在feign调用之前,我给他开启了一个拦截器 RequestInterceptor实现类 里面有使用到ServletRequestAttributes 获取请求数据

当feign开启熔断模式的时候,feign 调用会失败 (feign: hystrix: enabled: true)

原因:feign 使用的是线程池模式,当开启熔断的时候,feign 所在的服务端不在同一个线程,这时attributes取到的将会是空值

内部应用大致是 Thread 的线程,开启熔断以后请求的线程发生了变化,导致线程不一致了,所以 token 也没有传递过去。

解决方案:

将hystrix熔断方式:线程模式改为信号量模式

feign:
  hystrix:
    enabled: true
  client:
    config:
      default:
        connectTimeout: 100000
        readTimeout: 100000
hystrix:
 command:
   default:
     execution:
       isolation:
         thread:
           timeoutInMilliseconds: 30000
         strategy: SEMAPHORE  
ribbon:
  ReadTimeout: 60000
  ConnectTimeout: 60000

关键所在: strategy: SEMAPHORE

可以修改默认隔离策略为信号量模式:

加上线程信号量即可完美的解决,但是官方并不推荐这么使用。

推荐使用:

自定义策略

HystrixConcurrencyStrategy 是提供给开发者去自定义hystrix内部线程池及其队列,还提供了包装callable的方法,以及传递上下文变量的方法。

所以可以继承了HystrixConcurrencyStrategy,用来实现了自己的并发策略。

@Component
public class FeignHystrixConcurrencyStrategy extends HystrixConcurrencyStrategy {

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

    private HystrixConcurrencyStrategy delegate;

    public FeignHystrixConcurrencyStrategy() {
        try {
            this.delegate = HystrixPlugins.getInstance().getConcurrencyStrategy();
            if (this.delegate instanceof FeignHystrixConcurrencyStrategy) {
                // Welcome to singleton hell...
                return;
            }

            HystrixCommandExecutionHook commandExecutionHook =
                    HystrixPlugins.getInstance().getCommandExecutionHook();

            HystrixEventNotifier eventNotifier = HystrixPlugins.getInstance().getEventNotifier();
            HystrixMetricsPublisher metricsPublisher = HystrixPlugins.getInstance().getMetricsPublisher();
            HystrixPropertiesStrategy propertiesStrategy =
                    HystrixPlugins.getInstance().getPropertiesStrategy();
            this.logCurrentStateOfHystrixPlugins(eventNotifier, metricsPublisher, propertiesStrategy);

            HystrixPlugins.reset();
            HystrixPlugins instance = HystrixPlugins.getInstance();
            instance.registerConcurrencyStrategy(this);
            instance.registerCommandExecutionHook(commandExecutionHook);
            instance.registerEventNotifier(eventNotifier);
            instance.registerMetricsPublisher(metricsPublisher);
            instance.registerPropertiesStrategy(propertiesStrategy);
        } catch (Exception e) {
            log.error("Failed to register Sleuth Hystrix Concurrency Strategy", e);
        }
    }

    private void logCurrentStateOfHystrixPlugins(HystrixEventNotifier eventNotifier,
                                                 HystrixMetricsPublisher metricsPublisher,
                                                 HystrixPropertiesStrategy propertiesStrategy) {
        if (log.isDebugEnabled()) {
            log.debug("Current Hystrix plugins configuration is [" + "concurrencyStrategy ["
                    + this.delegate + "]," + "eventNotifier [" + eventNotifier + "]," + "metricPublisher ["
                    + metricsPublisher + "]," + "propertiesStrategy [" + propertiesStrategy + "]," + "]");
            log.debug("Registering Sleuth Hystrix Concurrency Strategy.");
        }
    }

    @Override
    public <T> Callable<T> wrapCallable(Callable<T> callable) {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        return new WrappedCallable<>(callable, requestAttributes);

参考链接

https://segmentfault.com/a/1190000021060393

上一篇:Hystrix线程池隔离与接口限流


下一篇:SpringCloud:Hystrix(服务熔断、服务降级)