53

Hystrix系列之ThreadLocal跨线程传递问题

 5 years ago
source link: http://www.jianshu.com/p/c60fe209a799?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

在Hystrix系列之前的文章中提到过,如果使用线程池模式,那么存在一个ThreadLocal变量跨线程传递的问题,即在主线程的ThreadLocal变量,无法在线程池中使用,不过Hystrix内部提供了解决方案,但是个人觉得这个方案不是那么友好。

解决方案

在Hystrix中,如果想在跨线程时共享数据,必须通过 HystrixRequestVariableDefault 申明变量

HystrixRequestVariableDefault name = new HystrixRequestVariableDefault();
name.set("占小狼");

其实在用法上,和 ThreadLocal 是一样的,只是需要对现有代码的大量改造。

看下这个方案的实现原理,先从 set 开始。

public void set(T value) {
  HystrixRequestContext.getContextForCurrentThread().state.put(this, new LazyInitializer<T>(this, value));
}

Hystrix内部通过 HystrixRequestContext 实现数据的跨线程传递, getContextForCurrentThread 得到的是当前线程的 HystrixRequestContext 对象,每个 HystrixRequestContext 对象都有一个对应 ConcurrentHashMap 变量 state ,负责保存通过 HystrixRequestVariableDefault 初始化的数据。

通过 set 方法,该对象和数据会被保存在一个当前线程所属的map中。为了实现数据的跨线程传递,只需要在初始化task的时候,把主线程的 HystrixRequestContext 变量保存起来,在task执行的时候,重新赋值到子线程的上下文中,这样在子线程中就可以顺利拿到这些数据。

Hystrix中通过 HystrixContextCallable 包装原始 Callable ,并使用 parentThreadState 保存了当前线程的 HystrixRequestContext 变量。

bM7femj.png!web

任务执行时,先保存子线程现有的 HystrixRequestContext 变量,再赋值主线程的 HystrixRequestContext 变量,任务执行完成后,重新还原子线程。

如果不想使用Hystrix这种方式实现,也可以使用Hystrix提供的插件方式重新包装task,通过实现 HystrixConcurrencyStrategy 类,重写 wrapCallable 方法,和Hystrix的实现原理类似。

比如提供一个继承 ThreadLocalXXXThreadLocal 类,那么业务方在使用时,就可以这样使用。

ThreadLocal name = new XXXThreadLocal();
name.set("占小狼");

这种方式看起来对已有逻辑只有一点小小的改动,对于新接入的也不那么陌生。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK