最近在做项目的时候发现,微服务使用feign相互之间调用时,存在session丢失的问题。例如,使用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();

此时requestAttributes会返回null,从而无法传递session信息,最终发现RequestContextHolder.getRequestAttributes(),该方法是从ThreadLocal变量里面取得对应信息的,这就找到问题原因了,是由于Hystrix熔断机制导致的。 
Hystrix有2个隔离策略: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类

其中就有我们熟悉的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();  }  }  }
}  

最后,将这个策略类作为bean配置到feign的配置类FeignClientsConfigurationCustom中

  @Beanpublic FeignHystrixConcurrencyStrategy feignHystrixConcurrencyStrategy() {return new FeignHystrixConcurrencyStrategy();}

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

feign调用session丢失解决方案相关推荐

  1. feign session 调用_springboot使用feign调用session传递失效解决方案

    标题 框架 version 1 springboot 1.5.9.release 2 springCloud 1.2.4.release 3 eureka 1.6.2 4 feign 9.5.0 前言 ...

  2. SpringCloud 各个微服务之间会话共享以及Feign调用会话共享

    目录 1.会话共享应用背景 2.SpringCloud各个微服务 (SpringBoot)应用之间会话共享 2.1.启动类或者Redis配置类加入Redis会话共享注解 2.2.配置Redis基本配置 ...

  3. 关于feign调用时,session丢失的解决方案

    最近在做公司微服务项目的时候发现,微服务使用feign相互之间调用时,存在session丢失的问题. 例如,使用feign调用某个远程API,这个远程API需要传递一个鉴权信息,我们可以把cookie ...

  4. [转]ASP.NET 状态服务 及 session丢失问题解决方案总结

    转自[http://blog.csdn.net/high_mount/archive/2007/05/09/1601854.aspx] 最近在开发一ASP.NET2.0系统时,在程序中做删除或创建文件 ...

  5. 【转】jquery文件上传插件uploadify在.NET中session丢失的解决方案

    2019独角兽企业重金招聘Python工程师标准>>> 基于jQuery和Flash的多文件上传插件uploadify的确很好用,具体配置和使用方法见以前的一篇文章: <一款基 ...

  6. 使用restTemplate进行feign调用new HttpEntity<>报错解决方案

    使用restTemplate进行feign调用new HttpEntity<>报错解决方案 问题背景 HttpEntity<>标红解决方案 心得 Lyric: 沙漠之中怎么会有 ...

  7. ASP.NET Session丢失问题原因及解决方案[转]

    不得不老调重弹 正常操作情况下会有ASP.NET Session丢失的情况出现.因为程序是在不停的被操作,排除Session超时的可能.另外,Session超时时间被设定成60分钟,不会这么快就超时的 ...

  8. session丢失原因 java_Session丢失原因与解决方案

    win2003 server下的IIS6默认设置下对每个运行在默认应用池中的工作者进程都会经过20多个小时后自动回收该进程, 造成保存在该进程中的session丢失. 因为Session,Applic ...

  9. 模式窗口window.open造成Session丢失的解决方法

    从 HTML 模式或无模式对话框可能没有打开同一进程中 InternetExplorer 窗口中打开,因此模式窗口中调用 window.open()方法打开具体页面,可能造成session 丢失.例如 ...

最新文章

  1. 数据分析师必看:5大概率分布,你了解多少?
  2. python学习音频-详解python播放音频的三种方法
  3. 怎么写redmine wiki
  4. 错误日志这样排查,干活更得劲了!!
  5. c语言字符串加减_C语言中指针的介绍
  6. Java并发控制基础篇 Thread继承类和Runnable实现类
  7. [新思路]Online DVD Rental! 美国在线DVD租赁
  8. jQuery : ddSlick 自定义select下拉框 custom drop down with images and description.
  9. ios 学习札记 细节(四)
  10. cascader回显
  11. 实验2 线性表的链式存储结构的实现及其应用
  12. 一个完整的软件开发过程到底需要哪些步骤?
  13. Legion使用:半自动化网络渗透工具
  14. echarts 简单词云制作,自定义图案词云echarts-wordcloud.js
  15. HDU 2246 考研路茫茫——考试大纲
  16. URL重定向(跳转)漏洞
  17. B 站,真香 ! ! !
  18. php网页增加音乐代码,js给网页加上背景音乐及选择音效的方法
  19. AtCoder Grand Round 012B Splatter Painting
  20. Cybersecurity Challenges In The Uptake Of Artifitial Intelligence in Autonomous Driving [1]

热门文章

  1. vue12 循环添加重复数据
  2. 2017python学习的第五天:模块
  3. React Native ios打包
  4. sass 在线转换器
  5. VC++ 中ListCtrl经验总结
  6. 判断手机所属三大运营商 移动、联通、电信
  7. Android性能优化案例研究(上)
  8. mfc CString 转 char *
  9. matlab点云配准(总结性)
  10. Sublime3 搭建C/C++环境