在Hystrix系列之前的文章中提到過,如果使用線程池模式,那么存在一個(gè)ThreadLocal變量跨線程傳遞的問題,即在主線程的ThreadLocal變量,無法在線程池中使用,不過Hystrix內(nèi)部提供了解決方案,但是個(gè)人覺得這個(gè)方案不是那么友好。
解決方案
在Hystrix中,如果想在跨線程時(shí)共享數(shù)據(jù),必須通過HystrixRequestVariableDefault申明變量
HystrixRequestVariableDefault name = new HystrixRequestVariableDefault();
name.set("占小狼");
其實(shí)在用法上,和ThreadLocal是一樣的,只是需要對(duì)現(xiàn)有代碼的大量改造。
看下這個(gè)方案的實(shí)現(xiàn)原理,先從set開始。
public void set(T value) {
HystrixRequestContext.getContextForCurrentThread().state.put(this, new LazyInitializer<T>(this, value));
}
Hystrix內(nèi)部通過HystrixRequestContext實(shí)現(xiàn)數(shù)據(jù)的跨線程傳遞,getContextForCurrentThread得到的是當(dāng)前線程的HystrixRequestContext對(duì)象,每個(gè)HystrixRequestContext對(duì)象都有一個(gè)對(duì)應(yīng)ConcurrentHashMap變量state,負(fù)責(zé)保存通過HystrixRequestVariableDefault初始化的數(shù)據(jù)。
通過set方法,該對(duì)象和數(shù)據(jù)會(huì)被保存在一個(gè)當(dāng)前線程所屬的map中。為了實(shí)現(xiàn)數(shù)據(jù)的跨線程傳遞,只需要在初始化task的時(shí)候,把主線程的HystrixRequestContext變量保存起來,在task執(zhí)行的時(shí)候,重新賦值到子線程的上下文中,這樣在子線程中就可以順利拿到這些數(shù)據(jù)。
Hystrix中通過HystrixContextCallable包裝原始Callable,并使用parentThreadState保存了當(dāng)前線程的HystrixRequestContext變量。

任務(wù)執(zhí)行時(shí),先保存子線程現(xiàn)有的HystrixRequestContext變量,再賦值主線程的HystrixRequestContext變量,任務(wù)執(zhí)行完成后,重新還原子線程。
如果不想使用Hystrix這種方式實(shí)現(xiàn),也可以使用Hystrix提供的插件方式重新包裝task,通過實(shí)現(xiàn)HystrixConcurrencyStrategy類,重寫wrapCallable方法,和Hystrix的實(shí)現(xiàn)原理類似。
比如提供一個(gè)繼承ThreadLocal的XXXThreadLocal類,那么業(yè)務(wù)方在使用時(shí),就可以這樣使用。
ThreadLocal name = new XXXThreadLocal();
name.set("占小狼");
這種方式看起來對(duì)已有邏輯只有一點(diǎn)小小的改動(dòng),對(duì)于新接入的也不那么陌生。