欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

详解feign调用session丢失解决方案

程序员文章站 2024-02-19 20:46:52
最近在做项目的时候发现,微服务使用feign相互之间调用时,存在session丢失的问题。例如,使用feign调用某个远程api,这个远程api需要传递一个鉴权信息,我们可...

最近在做项目的时候发现,微服务使用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" rel="external nofollow" >https://github.com/netflix/hystrix/wiki/plugins</a>.
 */
public abstract class hystrixconcurrencystrategy 

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

详解feign调用session丢失解决方案

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

 @bean
 public feignhystrixconcurrencystrategy feignhystrixconcurrencystrategy() {
  return new feignhystrixconcurrencystrategy();
 }

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。