美文网首页java高级开发技术积累
Feign调用session丢失解决方案

Feign调用session丢失解决方案

作者: 老鼠AI大米_Java全栈 | 来源:发表于2019-05-24 18:32 被阅读0次

    最近做一个项目,用到springCloud,结果遇到session丢失的问题,从zuul->feign->shiro->boot都有问题,默认情况下,zuul和feign在转发时都会丢掉header,于是花时间研究下。
    转:https://blog.csdn.net/Crystalqy/article/details/79083857

    自定义RequestInterceptor

    使用Feign调用某个远程API,这个远程API需要传递一个鉴权信息,我们可以把cookie里面的session信息放到Header里面,这个Header是动态的,跟你的HttpRequest相关,我们选择编写一个拦截器来实现Header的传递,也就是需要实现RequestInterceptor接口,具体代码如下:

    @Configuration  
    @EnableFeignClients(basePackages = "com.xxx.xxx.client")  
    public class FeignClientsConfigurationCustom implements RequestInterceptor {  
    
      @Override  
      public void apply(RequestTemplate template) {  
    
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();  
        if (requestAttributes == null) {  
          return;  
        }  
    
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();  
        Enumeration<String> headerNames = request.getHeaderNames();  
        if (headerNames != null) {  
          while (headerNames.hasMoreElements()) {  
            String name = headerNames.nextElement();  
            Enumeration<String> values = request.getHeaders(name);  
            while (values.hasMoreElements()) {  
              String value = values.nextElement();  
              template.header(name, value);  
            }  
          }  
        }  
    
      }  
    } 
    

    经过测试,上面的解决方案可以正常的使用;
    但是,当引入Hystrix熔断策略时,出现了一个新的问题:

    RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
    

    获取不到request信息,从而无法传递session信息,最终发现RequestContextHolder.getRequestAttributes()该方法是从ThreadLocal变量里面取得对应信息的,这就找到问题原因了,由于Hystrix熔断机制导致的。
    Hystrix有隔离策略:THREAD以及SEMAPHORE,当隔离策略为 THREAD 时,是没办法拿到 ThreadLocal 中的值的。

    因此有两种解决方案:

    方案一:调整格隔离策略

    hystrix.command.default.execution.isolation.strategy: SEMAPHORE
    

    这样配置后,Feign可以正常工作。

    但该方案不是特别好。原因是Hystrix官方强烈建议使用THREAD作为隔离策略! 可以参考官方文档说明

    方案二:自定义策略
    记得之前在研究zipkin日志追踪的时候,看到过Sleuth有自己的熔断机制,用来在thread之间传递Trace信息,Sleuth是可以拿到自己上下文信息的,查看源码找到了
    org.springframework.cloud.sleuth.instrument.hystrix.SleuthHystrixConcurrencyStrategy
    这个类,查看SleuthHystrixConcurrencyStrategy的源码,继承了HystrixConcurrencyStrategy,用来实现了自己的并发策略。

    /**
     * Abstract class for defining different behavior or implementations for concurrency related aspects of the system with default implementations.
     * <p>
     * For example, every {@link Callable} executed by {@link HystrixCommand} will call {@link #wrapCallable(Callable)} to give a chance for custom implementations to decorate the {@link Callable} with
     * additional behavior.
     * <p>
     * When you implement a concrete {@link HystrixConcurrencyStrategy}, you should make the strategy idempotent w.r.t ThreadLocals.
     * Since the usage of threads by Hystrix is internal, Hystrix does not attempt to apply the strategy in an idempotent way.
     * Instead, you should write your strategy to work idempotently.  See https://github.com/Netflix/Hystrix/issues/351 for a more detailed discussion.
     * <p>
     * See {@link HystrixPlugins} or the Hystrix GitHub Wiki for information on configuring plugins: <a
     * href="https://github.com/Netflix/Hystrix/wiki/Plugins">https://github.com/Netflix/Hystrix/wiki/Plugins</a>.
     */
    public abstract class HystrixConcurrencyStrategy 
    

    搜索发现有好几个地方继承HystrixConcurrencyStrategy类


    image.png

    其中就有我们熟悉的Spring Security和刚才提到的Sleuth都是使用了自定义的策略,同时由于Hystrix只允许有一个并发策略,因此为了不影响Spring Security和Sleuth,我们可以参考他们的策略实现自己的侧路,大致思路:
    将现有的并发策略作为新并发策略的成员变量;
    在新并发策略中,返回现有并发策略的线程池、Queue;
    代码如下:

    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.getInstance().registerConcurrencyStrategy(this);  
          HystrixPlugins.getInstance().registerCommandExecutionHook(commandExecutionHook);  
          HystrixPlugins.getInstance().registerEventNotifier(eventNotifier);  
          HystrixPlugins.getInstance().registerMetricsPublisher(metricsPublisher);  
          HystrixPlugins.getInstance().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);  
      }  
    
      @Override  
      public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,  
          HystrixProperty<Integer> corePoolSize, HystrixProperty<Integer> maximumPoolSize,  
          HystrixProperty<Integer> keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {  
        return this.delegate.getThreadPool(threadPoolKey, corePoolSize, maximumPoolSize, keepAliveTime,  
            unit, workQueue);  
      }  
    
      @Override  
      public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,  
          HystrixThreadPoolProperties threadPoolProperties) {  
        return this.delegate.getThreadPool(threadPoolKey, threadPoolProperties);  
      }  
    
      @Override  
      public BlockingQueue<Runnable> getBlockingQueue(int maxQueueSize) {  
        return this.delegate.getBlockingQueue(maxQueueSize);  
      }  
    
      @Override  
      public <T> HystrixRequestVariable<T> getRequestVariable(HystrixRequestVariableLifecycle<T> rv) {  
        return this.delegate.getRequestVariable(rv);  
      }  
    
      static class WrappedCallable<T> implements Callable<T> {  
        private final Callable<T> target;  
        private final RequestAttributes requestAttributes;  
    
        public WrappedCallable(Callable<T> target, RequestAttributes requestAttributes) {  
          this.target = target;  
          this.requestAttributes = requestAttributes;  
        }  
    
        @Override  
        public T call() throws Exception {  
          try {  
            RequestContextHolder.setRequestAttributes(requestAttributes);  
            return target.call();  
          } finally {  
            RequestContextHolder.resetRequestAttributes();  
          }  
        }  
      }  
    } 
    

    至此,结合FeignClientsConfigurationCustom配置feign调用session丢失的问题完美解决。

    相关文章

      网友评论

        本文标题:Feign调用session丢失解决方案

        本文链接:https://www.haomeiwen.com/subject/oumnzqtx.html