最近在做項(xiàng)目的時(shí)候發(fā)現(xiàn),微服務(wù)使用feign相互之間調(diào)用時(shí),存在session丟失的問(wèn)題。例如,使用feign調(diào)用某個(gè)遠(yuǎn)程api,這個(gè)遠(yuǎn)程api需要傳遞一個(gè)鑒權(quán)信息,我們可以把cookie里面的session信息放到header里面,這個(gè)header是動(dòng)態(tài)的,跟你的httprequest相關(guān),我們選擇編寫(xiě)一個(gè)攔截器來(lái)實(shí)現(xiàn)header的傳遞,也就是需要實(shí)現(xiàn)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); } } } } } |
經(jīng)過(guò)測(cè)試,上面的解決方案可以正常的使用;
但是,當(dāng)引入hystrix熔斷策略時(shí),出現(xiàn)了一個(gè)新的問(wèn)題:
1
|
requestattributes requestattributes = requestcontextholder.getrequestattributes(); |
此時(shí)requestattributes會(huì)返回null,從而無(wú)法傳遞session信息,最終發(fā)現(xiàn)requestcontextholder.getrequestattributes(),該方法是從threadlocal變量里面取得對(duì)應(yīng)信息的,這就找到問(wèn)題原因了,是由于hystrix熔斷機(jī)制導(dǎo)致的。
hystrix有2個(gè)隔離策略:thread以及semaphore,當(dāng)隔離策略為 thread 時(shí),是沒(méi)辦法拿到 threadlocal 中的值的。
因此有兩種解決方案:
方案一:調(diào)整格隔離策略:
1
|
hystrix.command. default .execution.isolation.strategy: semaphore |
這樣配置后,feign可以正常工作。
但該方案不是特別好。原因是hystrix官方強(qiáng)烈建議使用thread作為隔離策略! 可以參考官方文檔說(shuō)明。
方案二:自定義策略
記得之前在研究zipkin日志追蹤的時(shí)候,看到過(guò)sleuth有自己的熔斷機(jī)制,用來(lái)在thread之間傳遞trace信息,sleuth是可以拿到自己上下文信息的,查看源碼找到了
org.springframework.cloud.sleuth.instrument.hystrix.sleuthhystrixconcurrencystrategy
這個(gè)類,查看sleuthhystrixconcurrencystrategy的源碼,繼承了hystrixconcurrencystrategy,用來(lái)實(shí)現(xiàn)了自己的并發(fā)策略。
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.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 |
搜索發(fā)現(xiàn)有好幾個(gè)地方繼承了hystrixconcurrencystrategy類
其中就有我們熟悉的spring security和剛才提到的sleuth都是使用了自定義的策略,同時(shí)由于hystrix只允許有一個(gè)并發(fā)策略,因此為了不影響spring security和sleuth,我們可以參考他們的策略實(shí)現(xiàn)自己的策略,大致思路:
將現(xiàn)有的并發(fā)策略作為新并發(fā)策略的成員變量;
在新并發(fā)策略中,返回現(xiàn)有并發(fā)策略的線程池、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(); } } } } |
最后,將這個(gè)策略類作為bean配置到feign的配置類feignclientsconfigurationcustom中
1
2
3
4
|
@bean public feignhystrixconcurrencystrategy feignhystrixconcurrencystrategy() { return new feignhystrixconcurrencystrategy(); } |
至此,結(jié)合feignclientsconfigurationcustom配置feign調(diào)用session丟失的問(wèn)題完美解決。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。
原文鏈接:https://blog.csdn.net/zl1zl2zl3/article/details/79084368