Feign调用session丢失解决方案

最近做一个项目,用到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丢失的问题完美解决。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,634评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,951评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,427评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,770评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,835评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,799评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,768评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,544评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,979评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,271评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,427评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,121评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,756评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,375评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,579评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,410评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,315评论 2 352

推荐阅读更多精彩内容