Hystrix 隔離策略細(xì)粒度控制
Hystrix 實現(xiàn)資源隔離煎源,有兩種策略:
- 線程池隔離
- 信號量隔離
對資源隔離這一塊東西联喘,其實可以做一定細(xì)粒度的一些控制纳鼎。
execution.isolation.strategy
指定了 HystrixCommand.run() 的資源隔離策略:THREAD
or SEMAPHORE
果元,一種基于線程池契耿,一種基于信號量靡羡。
// to use thread isolation
HystrixCommandProperties.Setter().withExecutionIsolationStrategy(ExecutionIsolationStrategy.THREAD)
// to use semaphore isolation
HystrixCommandProperties.Setter().withExecutionIsolationStrategy(ExecutionIsolationStrategy.SEMAPHORE)
線程池機(jī)制系洛,每個 command 運行在一個線程中,限流是通過線程池的大小來控制的略步;信號量機(jī)制描扯,command 是運行在調(diào)用線程中,通過信號量的容量來進(jìn)行限流趟薄。
如何在線程池和信號量之間做選擇绽诚?
默認(rèn)的策略就是線程池。
線程池其實最大的好處就是對于網(wǎng)絡(luò)訪問請求杭煎,如果有超時的話恩够,可以避免調(diào)用線程阻塞住。
而使用信號量的場景羡铲,通常是針對超大并發(fā)量的場景下蜂桶,每個服務(wù)實例每秒都幾百的 QPS
,那么此時你用線程池的話也切,線程一般不會太多屎飘,可能撐不住那么高的并發(fā),如果要撐住贾费,可能要耗費大量的線程資源钦购,那么就是用信號量,來進(jìn)行限流保護(hù)褂萧。一般用信號量常見于那種基于純內(nèi)存的一些業(yè)務(wù)邏輯服務(wù)押桃,而不涉及到任何網(wǎng)絡(luò)訪問請求。
command key & command group
我們使用線程池隔離导犹,要怎么對依賴服務(wù)唱凯、依賴服務(wù)接口羡忘、線程池三者做劃分呢?
每一個 command磕昼,都可以設(shè)置一個自己的名稱 command key卷雕,同時可以設(shè)置一個自己的組 command group。
private static final Setter cachedSetter = Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
.andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld"));
public CommandHelloWorld(String name) {
super(cachedSetter);
this.name = name;
}
command group 是一個非常重要的概念票从,默認(rèn)情況下漫雕,就是通過 command group 來定義一個線程池的,而且還會通過 command group 來聚合一些監(jiān)控和報警信息峰鄙。同一個 command group 中的請求浸间,都會進(jìn)入同一個線程池中。
command thread pool
ThreadPoolKey 代表了一個 HystrixThreadPool吟榴,用來進(jìn)行統(tǒng)一監(jiān)控魁蒜、統(tǒng)計、緩存吩翻。默認(rèn)的 ThreadPoolKey 就是 command group 的名稱兜看。每個 command 都會跟它的 ThreadPoolKey 對應(yīng)的 ThreadPool 綁定在一起。
如果不想直接用 command group狭瞎,也可以手動設(shè)置 ThreadPool 的名稱铣减。
private static final Setter cachedSetter = Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
.andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld"))
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("HelloWorldPool"));
public CommandHelloWorld(String name) {
super(cachedSetter);
this.name = name;
}
command key & command group & command thread pool
command key ,代表了一類 command脚作,一般來說葫哗,代表了底層的依賴服務(wù)的一個接口。
command group 球涛,代表了某一個底層的依賴服務(wù)劣针,這是很合理的,一個依賴服務(wù)可能會暴露出來多個接口亿扁,每個接口就是一個 command key捺典。command group 在邏輯上去組織起來一堆 command key 的調(diào)用、統(tǒng)計信息从祝、成功次數(shù)襟己、timeout 超時次數(shù)、失敗次數(shù)等牍陌,可以看到某一個服務(wù)整體的一些訪問情況擎浴。一般來說,推薦根據(jù)一個服務(wù)區(qū)劃分出一個線程池毒涧,command key 默認(rèn)都是屬于同一個線程池的贮预。
比如說你以一個服務(wù)為粒度,估算出來這個服務(wù)每秒的所有接口加起來的整體 QPS
在 100 左右,你調(diào)用這個服務(wù)仿吞,當(dāng)前這個服務(wù)部署了 10 個服務(wù)實例滑频,每個服務(wù)實例上,其實用這個 command group 對應(yīng)這個服務(wù)唤冈,給一個線程池峡迷,量大概在 10 個左右就可以了,你對整個服務(wù)的整體的訪問 QPS 就大概在每秒 100 左右你虹。
但是绘搞,如果說 command group 對應(yīng)了一個服務(wù),而這個服務(wù)暴露出來的幾個接口售葡,訪問量很不一樣看杭,差異非常之大忠藤。你可能就希望在這個服務(wù) command group 內(nèi)部挟伙,包含的對應(yīng)多個接口的 command key,做一些細(xì)粒度的資源隔離模孩。就是說尖阔,對同一個服務(wù)的不同接口,使用不同的線程池榨咐。
command key -> command group
command key -> 自己的 thread pool key
邏輯上來說介却,多個 command key 屬于一個command group,在做統(tǒng)計的時候块茁,會放在一起統(tǒng)計齿坷。每個 command key 有自己的線程池,每個接口有自己的線程池数焊,去做資源隔離和限流永淌。
說白點,就是說如果你的 command key 要用自己的線程池佩耳,可以定義自己的 thread pool key遂蛀,就 ok 了。
coreSize
設(shè)置線程池的大小干厚,默認(rèn)是 10李滴。一般來說,用這個默認(rèn)的 10 個線程大小就夠了蛮瞄。
HystrixThreadPoolProperties.Setter().withCoreSize(int value);
queueSizeRejectionThreshold
如果說線程池中的 10 個線程都在工作中所坯,沒有空閑的線程來做其它的事情,此時再有請求過來挂捅,會先進(jìn)入隊列積壓包竹。如果說隊列積壓滿了,再有請求過來,就直接 reject周瞎,拒絕請求苗缩,執(zhí)行 fallback 降級的邏輯,快速返回声诸。
控制 queue 滿了之后 reject 的 threshold酱讶,因為 maxQueueSize 不允許熱修改,因此提供這個參數(shù)可以熱修改彼乌,控制隊列的最大大小泻肯。
HystrixThreadPoolProperties.Setter().withQueueSizeRejectionThreshold(int value);
execution.isolation.semaphore.maxConcurrentRequests
設(shè)置使用 SEMAPHORE 隔離策略的時候允許訪問的最大并發(fā)量,超過這個最大并發(fā)量慰照,請求直接被 reject灶挟。
這個并發(fā)量的設(shè)置,跟線程池大小的設(shè)置毒租,應(yīng)該是類似的稚铣,但是基于信號量的話,性能會好很多墅垮,而且 Hystrix 框架本身的開銷會小很多惕医。
默認(rèn)值是 10,盡量設(shè)置的小一些算色,因為一旦設(shè)置的太大抬伺,而且有延時發(fā)生,可能瞬間導(dǎo)致 tomcat 本身的線程資源被占滿灾梦。
HystrixCommandProperties.Setter().withExecutionIsolationSemaphoreMaxConcurrentRequests(int value);