hystrix執行緒池隔離的原理與驗證
引子
幸福很簡單:
今天專案半年規劃被通過,終於可以早點下班。先坐公交,全程開著燈,買了了幾天的書竟然有時間看了。半小時後,公交到站,換乘大巴車。車還等著上人的功夫,有昏暗的燈光,可以繼續看會兒書。過會兒車跑起來了,燈關了。我合上書,頭靠著車窗,眼睛看著窗外,腦子想著怎麼把書裡的東西用到工作中進行知行合一。想著想著出了神,突然聽到報我們小區的名字,趕緊下了車,剛好沒坐過站。
回家一看,那個聲稱今天會下班很晚的人果然比我還晚。邊洗漱邊想著上週末,和小鮮肉一起看了動畫片。小鮮肉非要我買培根。因為他想像動畫片裡一樣用兩個荷包蛋做眼睛,培根做嘴巴。於是晚上睡覺的時候,我下單買了培根,早上7點快遞送了來。我就和小鮮肉一起做早餐。他說想吃糖口味的蛋,於是我們改良了一下把荷包蛋眼睛變成攤雞蛋大臉。本來小鮮肉說只要一片培根。我想一包都打開了,乾脆一起煎了。結果因為是自己動手,小鮮肉只好多吃,結果吃培根吃的一天都感覺油膩。想著這周,一包培根要分到一天三頓裡,加上配菜,和小鮮肉一起做出三種花樣來。關鍵是小鮮肉到週末比我還忙,8點開始跆拳道,一天各種興趣班。他想自己做早餐得早起。
洗漱完,吹完頭髮。看看洗澡水又燒的差不多了。這次終於輪到我發那條訊息:“洗澡水夠了”。然後拔掉燒水的電源確保安全。竟然有時間貼上面膜再看會書,滿足。
原來幸福就是:其實很忙,但是能擠出點時間乾點自己的事情,提升提升自己。
hystrix隔離原理
hystrix可以完成隔離、限流、熔斷、降級這些常用保護功能。這四個功能可以這麼來理解:
hystrix的隔離分為執行緒池隔離和訊號量隔離。
訊號量隔離原理
訊號量隔離就是hystrix的限流功能。雖然名字叫隔離,實際上它是通過訊號量來實現的。而訊號量說白了就是個計數器。計數器計算達到設定的閾值,直接就做異常處理。
ratelimiter的令牌桶演算法和漏桶演算法,都是直接對請求量來計數。只是令牌桶演算法可以將前面一段時間沒有用掉的請求量允許餘額拿過繼續用。而漏桶演算法一段時間就允許這麼多,前面沒用掉的也不能用了。
而hystrix訊號量隔離限制的是tomcat等Web容器執行緒數,一段時間僅僅能支援這麼多。多餘的請求再來請求執行緒資源,就被拒絕了。所以是一種“曲徑通幽”的限流方式。因為實際是通過隔離了部分容器執行緒資源,也算是一種隔離方式。
執行緒池隔離原理
訊號量隔離只是起了個限制作用,它的保護能力有限:如果下游服務有問題,長時間不返回結果。本身訊號量隔離對這個單個請求是起不到任何作用的。它只能限制這樣的請求太多了就拒絕,不讓整個服務掛。
為了解決這個問題,hystrix又產生了執行緒池隔離。這種隔離方式是通過引入額外執行緒的方式。對原來的web容器執行緒做管理控制:如果一個執行緒超時未返回,則熔斷。既然引入額外的執行緒就涉及執行緒池管理、執行緒的上下文切換這些額外的開銷。所以相比訊號量隔離,執行緒池隔離成本更高。
熔斷原理
隔離不但可以做保護,還可以做統計:成功了多少失敗了多少。既然有統計資料了,它就可以進一步處理:失敗太多了,說明現在有問題,執行完了再發現失敗太浪費資源,乾脆就先不讓worker執行緒執行了。過段時間再試試。這就是斷路器模式,也就是熔斷的原理。
降級原理
任何異常需要熔斷的場景,為的都是反正都是錯,乾脆把這資源省了。直接返回一個預定的錯誤。這個熔斷後返回設定錯誤的過程就是降級。
資源保護的流程
從上面來看所謂的hystrix的四大功能:限流、隔離、熔斷和降級。只是完成了一整個對資源保護的生命週期。來看看對應的BPMN流程圖:
訊號量隔離的流程
執行緒池隔離的流程
現在大家請回答我一個問題:我上面說的是對的嗎?
hystrix隔離驗證
採用淘金式的思維,不要別人說什麼都信。網上很多技術部落格裡說的都是錯的。我的文章也有可能是錯的,事實上我說的很多東西都帶有自己腦補的成分。
既然不確定是否是對的,就要去驗證。先驗證
1>hystrix隔離確實能限制資源
2>訊號量隔離採用的Web容器的執行緒池,而執行緒池隔離採用的是自己獨立的執行緒池。
本次驗證,Web容器使用的是spring boot內嵌的jetty。程式碼已經上傳github:
https://github.com/xiexiaojing/yuna
訊號量隔離驗證
隔離hystrix配置
import com.netflix.hystrix.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class DemoHystrixCommand extends HystrixCommand<String> {
private static final Logger logger = LoggerFactory.getLogger(DemoHystrixCommand.class);
private String poolName;
public DemoHystrixCommand() {
super(Setter.withGroupKey(
//服務分組
HystrixCommandGroupKey.Factory.asKey("DemoGroup"))
//執行緒分組
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("DemoPool"))
//執行緒池配置
.andThreadPoolPropertiesDefaults(
HystrixThreadPoolProperties.Setter()
.withCoreSize(2)
.withKeepAliveTimeMinutes(5)
.withMaxQueueSize(2)
.withQueueSizeRejectionThreshold(10))
//執行緒池隔離
.andCommandPropertiesDefaults(
HystrixCommandProperties.Setter()
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE)
)
);
}
@Override
protected String run() throws Exception {
logger.info(poolName + ":我傷心我無奈,可是我默默等待");
TimeUnit.MILLISECONDS.sleep(100);
return poolName + "-run:緣分就是一生的等待";
}
public void setPoolName(String poolName) {
this.poolName = poolName;
}
}
呼叫方
import com.brmayi.yuna.util.DemoHystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
public class HystrixController {
@Autowired
private ApplicationContext applicationContext;
@GetMapping("/hystrix")
public String hystrix(String poolName) throws Exception {
DemoHystrixCommand demoHystrixCommand = applicationContext.getBean(DemoHystrixCommand.class);
demoHystrixCommand.setPoolName(poolName);
return demoHystrixCommand.execute();
}
}
驗證
多點幾次
多點幾次
看日誌,日誌字首裡Web容器的其他日誌執行緒號和請求hystrix的執行緒號規則一致,可說明是Web容器的執行緒。
注意看,不管是倩倩還是萍兒,它們的執行緒數都沒有超過最大執行緒數
執行緒池隔離驗證
只要將隔離hystrix配置的
HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE
改成
HystrixCommandProperties.ExecutionIsolationStrategy.THREAD
重啟後重覆上面驗證步驟
看執行緒池名變成了隔離hystrix配置的執行緒名規則!並且不管是倩倩還是萍兒,它們的執行緒數都沒有超過最大執行緒數。
以上實驗說明
1>hystrix隔離確實能限制資源
2>訊號量隔離採用的Web容器的執行緒池,而執行緒池隔離採用的是自己獨立的執行緒池。
成立。
其他部分限於篇幅,我就不驗證了。這裡用到了打日誌的方法驗證執行緒池情況。如果在生產環境,實際上我們是需要對執行緒池情況做監控的。可以使用java.lang.management包裡的工具註冊監控。我們平時使用falcon這樣的工具來檢視,原理也是先通過java.lang.management包裡的工具註冊上報資訊到服務端採集的。如果自己想檢視監控結果,可以用jdk自帶的jvisualvm安裝一個com-sun-tools-visualvm-modules-mbeans.nbm外掛來看。
總結
本篇文章的驗證部分很粗糙,限於篇幅,沒有把所有需要驗證的點覆蓋全。想驗證我花的hystrix資源保護生命週期的圖,至少要結合原始碼和驗證兩方面。先當留作業了,有時間我把詳細過程補