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

作者:zl1zl2zl3 时间:2021-08-29 20:38:20 

最近在做项目的时候发现,微服务使用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丢失的问题完美解决。

来源:https://blog.csdn.net/zl1zl2zl3/article/details/79084368

标签:feign,session,丢失
0
投稿

猜你喜欢

  • mybatis源码解读之executor包懒加载功能 

    2022-09-17 00:28:05
  • Jmeter自定义函数base64加密实现过程解析

    2021-10-21 14:04:00
  • C#编程之事务用法

    2023-08-20 05:50:37
  • SpringBoot接口中如何直接返回图片数据

    2023-04-22 13:10:24
  • 基于Android SQLite的升级详解

    2021-06-24 05:26:05
  • Spring创建Bean的生命周期详析

    2022-01-27 06:33:26
  • Android XML設置屏幕方向(android:screenOrientation)详解

    2021-09-08 09:46:35
  • 详解Android中App的启动界面Splash的编写方法

    2021-11-20 23:14:46
  • SpringCloud中Eureka的配置及使用讲解

    2022-04-19 19:16:28
  • 在IDEA中安装MyBatis Log Plugin插件,执行mybatis的sql语句(推荐)

    2022-02-17 08:04:43
  • spring整合redis缓存并以注解(@Cacheable、@CachePut、@CacheEvict)形式使用

    2022-04-25 01:46:14
  • 学习Java之如何正确地向上转型与向下转型

    2022-10-12 09:58:10
  • Android判断11位手机号码的方法(正则表达式)

    2022-03-06 03:17:43
  • Android Jetpack组件中LifeCycle作用详细介绍

    2022-05-14 04:56:15
  • Android手机开发设计之记事本功能

    2023-12-10 21:47:00
  • Android添加用户组及自定义App权限的方法

    2022-09-15 20:08:53
  • android自定义进度条渐变圆形

    2022-07-15 02:04:14
  • Android Button按钮点击背景和文字变化操作

    2023-10-27 22:27:22
  • C#实现农历日历的方法

    2022-08-17 21:27:29
  • Android SharedPreferences四种操作模式使用详解

    2021-06-06 00:15:08
  • asp之家 软件编程 m.aspxhome.com