最近在做项目的时候发现,微服务使用feign相互之间调用时,存在session丢失的问题。例如,使用feign调用某个远程api,这个远程api需要传递一个鉴权信息,我们可以把cookie里面的session信息放到header里面,这个header是动态的,跟你的httprequest相关,我们选择编写一个拦截器来实现header的传递,也就是需要实现requestinterceptor接口,具体代码如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
@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熔断策略时,出现了一个新的问题:
|
1 |
requestattributes requestattributes = requestcontextholder.getrequestattributes(); |
此时requestattributes会返回null,从而无法传递session信息,最终发现requestcontextholder.getrequestattributes(),该方法是从threadlocal变量里面取得对应信息的,这就找到问题原因了,是由于hystrix熔断机制导致的。
hystrix有2个隔离策略:thread以及semaphore,当隔离策略为 thread 时,是没办法拿到 threadlocal 中的值的。
因此有两种解决方案:
方案一:调整格隔离策略:
|
1 |
hystrix测试数据mand. default .execution.isolation.strategy: semaphore |
这样配置后,feign可以正常工作。
但该方案不是特别好。原因是hystrix官方强烈建议使用thread作为隔离策略! 可以参考官方文档说明。
方案二:自定义策略
记得之前在研究zipkin日志追踪的时候,看到过sleuth有自己的熔断机制,用来在thread之间传递trace信息,sleuth是可以拿到自己上下文信息的,查看源码找到了
org.springframework.cloud.sleuth.instrument.hystrix.sleuthhystrixconcurrencystrategy
这个类,查看sleuthhystrixconcurrencystrategy的源码,继承了hystrixconcurrencystrategy,用来实现了自己的并发策略。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/** * 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测试数据/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测试数据/netflix/hystrix/wiki/plugins" rel="external nofollow" >https://github测试数据/netflix/hystrix/wiki/plugins</a>. */ public abstract class hystrixconcurrencystrategy |
搜索发现有好几个地方继承了hystrixconcurrencystrategy类
其中就有我们熟悉的spring security和刚才提到的sleuth都是使用了自定义的策略,同时由于hystrix只允许有一个并发策略,因此为了不影响spring security和sleuth,我们可以参考他们的策略实现自己的策略,大致思路:
将现有的并发策略作为新并发策略的成员变量;
在新并发策略中,返回现有并发策略的线程池、queue;
代码如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
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中
|
1 2 3 4 |
@bean public feignhystrixconcurrencystrategy feignhystrixconcurrencystrategy() { return new feignhystrixconcurrencystrategy(); } |
至此,结合feignclientsconfigurationcustom配置feign调用session丢失的问题完美解决。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
原文链接:https://blog.csdn.net/zl1zl2zl3/article/details/79084368
查看更多关于详解feign调用session丢失解决方案的详细内容...