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

最近在做项目的时候发现,微服务使用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 headerNames = request.getHeaderNames();

if (headerNames != null) {

while (headerNames.hasMoreElements()) {

String name = headerNames.nextElement();

Enumeration 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.

*

* 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.

*

* 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.

*

* See {@link HystrixPlugins} or the Hystrix GitHub Wiki for information on configuring plugins:

* href="https://github.com/Netflix/Hystrix/wiki/Plugins" rel="external nofollow" >https://github.com/Netflix/Hystrix/wiki/Plugins

.

*/

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 Callable wrapCallable(Callable callable) {

RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

return new WrappedCallable<>(callable, requestAttributes);

}

@Override

public ThreadPoolExecutor getThreadPool(HystrixThreadPoolKey threadPoolKey,

HystrixProperty corePoolSize, HystrixProperty maximumPoolSize,

HystrixProperty keepAliveTime, TimeUnit unit, BlockingQueue 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 getBlockingQueue(int maxQueueSize) {

return this.delegate.getBlockingQueue(maxQueueSize);

}

@Override

public HystrixRequestVariable getRequestVariable(HystrixRequestVariableLifecycle rv) {

return this.delegate.getRequestVariable(rv);

}

static class WrappedCallable implements Callable {

private final Callable target;

private final RequestAttributes requestAttributes;

public WrappedCallable(Callable 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丢失的问题完美解决。

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值