好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

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

最近在做项目的时候发现,微服务使用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丢失解决方案的详细内容...

  阅读:17次