在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
變量。
任務執行時,先保存子線程現有的HystrixRequestContext
變量,再賦值主線程的HystrixRequestContext
變量,任務執行完成后,重新還原子線程。
如果不想使用Hystrix這種方式實現,也可以使用Hystrix提供的插件方式重新包裝task,通過實現HystrixConcurrencyStrategy
類,重寫wrapCallable
方法,和Hystrix的實現原理類似。
比如提供一個繼承ThreadLocal
的XXXThreadLocal
類,那么業務方在使用時,就可以這樣使用。
ThreadLocal name = new XXXThreadLocal();
name.set("占小狼");
這種方式看起來對已有邏輯只有一點小小的改動,對于新接入的也不那么陌生。