Hystrix組件提供了兩種隔離的解決方案:線程池隔離和信號量隔離。兩種隔離方式都是限制對共享資源的併發訪問量,線程在就緒狀態、運行狀態、阻塞狀態、終止狀態間轉變時需要由操作系統調度,佔用很大的性能消耗;而信號量是在訪問共享資源時,進行tryAcquire,tryAcquire成功才允許訪問共享資源。
線程池隔離
不同的業務線之間選擇用線程池隔離,降低互相影響的概率。設置隔離策略爲線程池隔離:
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD));在Hystrix內部,是根據properties.executionIsolationStrategy().get()這個字段判斷隔離級別。如在getRunObservableDecoratedForMetricsAndErrorHandling這個方法中會先判斷是不是線程池隔離,如果是就獲取線程池,如果不是則進行信號量隔離的操作。
如果是線程池隔離,還需要設置線程池的相關參數如:線程池名字andThreadPoolKey , coreSize(核心線程池大小) , KeepAliveTimeMinutes(線程存存活時間),MaxQueueSize(最大隊列長度),QueueSizeRejectionThreshold(拒絕執行的閥值)等等。
.andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withCoreSize(resourcesManager.getThreadPoolProperties(platformProtocol.getAppId()).getCoreSize())
.withKeepAliveTimeMinutes(resourcesManager.getThreadPoolProperties(platformProtocol.getAppId()).getKeepAliveSeconds())
.withMaxQueueSize(resourcesManager.getThreadPoolProperties(platformProtocol.getAppId()).getMaxQueueSize())
.withKeepAliveTimeMinutes(resourcesManager.getThreadPoolProperties(platformProtocol.getAppId()).getKeepAliveSeconds())
.withMaxQueueSize(resourcesManager.getThreadPoolProperties(platformProtocol.getAppId()).getMaxQueueSize())
.withQueueSizeRejectionThreshold(resourcesManager.getThreadPoolProperties(platformProtocol.getAppId()).getQueueSizeRejectionThreshold()))
threadPoolKey 也是線程池的名字的前綴,默認前綴是 hystrix 。在Hystrix中,核心線程數和最大線程數是一致的,減少線程臨時創建和銷燬帶來的性能開銷。線程池的默認參數都在HystrixThreadPoolProperties中,重點講解一下參數queueSizeRejectionThreshold
和maxQueueSize 。queueSizeRejectionThreshold默認值是5,允許在隊列中的等待的任務數量。maxQueueSize默認值是-1,隊列大小。如果是Fast Fail 應用,建議使用默認值。線程池飽滿後直接拒絕後續的任務,不再進行等待。代碼如下HystrixThreadPool類中:
@Override
public boolean isQueueSpaceAvailable() {
if (queueSize <= 0) {
// we don't have a queue so we won't look for space but instead
// let the thread-pool reject or not
return true;
} else {
return threadPool.getQueue().size() < properties.queueSizeRejectionThreshold().get();
}
public boolean isQueueSpaceAvailable() {
if (queueSize <= 0) {
// we don't have a queue so we won't look for space but instead
// let the thread-pool reject or not
return true;
} else {
return threadPool.getQueue().size() < properties.queueSizeRejectionThreshold().get();
}
}
線程池一旦創建完成,相關參數就不會更改,存放在靜態的ConcurrentHashMap中,key是對應的commandKey 。而queueSizeRejectionThreshold是每個命令都是設置的。
線程池的相關參數都保存在HystrixThreadPool這個類文件中,線程池的創建方法getThreadPool則在HystrixConcurrencyStrategy類文件中。從getThreadPool方法可以看出線程池的名字就是hystrix-threadPoolKey-threadNumber.
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r, "hystrix-" + threadPoolKey.name() + "-" + threadNumber.incrementAndGet());
thread.setDaemon(true);
return thread;
public Thread newThread(Runnable r) {
Thread thread = new Thread(r, "hystrix-" + threadPoolKey.name() + "-" + threadNumber.incrementAndGet());
thread.setDaemon(true);
return thread;
}
在HystrixThreadPool實現類的構造方法中,併發HystrixConcurrencyStrategy實例是通過HystrixPlugins獲取的,所以可以通過HystrixPlugins設置自定義插件。具體的HystrixPlugins如何使用,會在後面章節中講解。
線程池的創建
前面說了,在Hystrix內部大部分類都是單實例,同樣ThreadPool也不例外,也是單實例。並且相同commandKey的依賴還必須是使用同一個線程池。這就需要把ThreadPool保存在一個靜態的map中,key是commandKey,同時要保證線程安全,Hytstrix使用了ConcurrentHashMap。關於爲什麼不適用HashTable保證線程安全問題的疑問請自行Google。線程池的創建在HystrixThreadPool這個類文件中的內部類Factory中的getInstance方法。
/* package */final static ConcurrentHashMap<String, HystrixThreadPool> threadPools = new ConcurrentHashMap<String, HystrixThreadPool>();
String key = threadPoolKey.name();
// this should find it for all but the first time
HystrixThreadPool previouslyCached = threadPools.get(key);
if (previouslyCached != null) {
return previouslyCached;
}
// if we get here this is the first time so we need to initialize
synchronized (HystrixThreadPool.class) {
if (!threadPools.containsKey(key)) {
threadPools.put(key, new HystrixThreadPoolDefault(threadPoolKey, propertiesBuilder));
}
}
return threadPools.get(key);
線程池的使用
HystrixCommand類的execute()內部調用了queue() ,queue又調用了父類AbstractCommand的toObservable方法,toObservable方法處理了是否可緩存問題後,交給了getRunObservableDecoratedForMetricsAndErrorHandling方法,這個方法設置了一系列的executionHook之後,交給了getExecutionObservableWithLifecycle,這個方法通過getExecutionObservable()獲取了執行器。getExecutionObservable()是個抽象方法,具體實現放在了子類:HystrixCommand和HystrixObservableCommand類中。下面是HystrixCommand類中的getExecutionObservable方法實現:
final protected Observable<R> getExecutionObservable() {
return Observable.create(new OnSubscribe<R>() {
@Override
public void call(Subscriber<? super R> s) {
try {
s.onNext(run());
s.onCompleted();
} catch (Throwable e) {
s.onError(e);
}
}
});
}
return Observable.create(new OnSubscribe<R>() {
@Override
public void call(Subscriber<? super R> s) {
try {
s.onNext(run());
s.onCompleted();
} catch (Throwable e) {
s.onError(e);
}
}
});
}
在這個Call方法中執行了具體的業務邏輯run() ;