Hystrix是Netfilx開源的一款針對分布式系統延遲和容錯的架構,目的是用來隔離分布式故障,它提供了線程和信号量隔離,可以減少不同服務競争資源造成的影響,提供了優雅降級機制以及熔斷機制來保持高可用。
當大多數人在使用Tomcat時,多個HTTP服務會共享一個線程池,假設其中一個HTTP 服務通路的資料庫響應非常慢,這将造成服務響應時間延遲增加,大多數線程阻塞等待資料響應傳回,導緻整個Tomcat線程池都被該服務占用,甚至拖垮整個Tomcat。是以,如果我們能把不同HTTP服務隔離到不同的線程池,則某個HTTP服務的線程池滿了也不會對其他服務造成災難性故障。這就需要線程隔離或者信号量隔離來實作了。
線程池隔離
下面來看使用Hystrix實作線程隔離:
看上圖,tomcat會為不同的http服務設定不同的線程池,同時為不同的分布式服務調用設定不同的線程池。
假設我們要調用一個擷取問題的服務。可以通過一個指令來實作,我們可以把其包裝成一個指令模式,模式中繼承HystrixCommand,重寫線程隔離以及配置的一些資訊。代碼如下:
public class HystrixProblemServiceCommand extends HystrixCommand> {
// 組名
private static final String GROUP_KEY = "qa";
// 指令辨別名
private static final String COMMAND_KEY = "query";
// 線程池名
private static final String THREADPOOL_KEY = "qa-pool";
@Autowired
private ProblemService problemService;
protected HystrixProblemServiceCommand() {
super(setter());
}
private static Setter setter() {
// 服務分組
HystrixCommandGroupKey hystrixCommandGroupKey = HystrixCommandGroupKey.Factory.asKey(GROUP_KEY);
// 服務唯一辨別
HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey(COMMAND_KEY);
// 線程池名稱
HystrixThreadPoolKey threadPoolKey = HystrixThreadPoolKey.Factory.asKey(THREADPOOL_KEY);
// 線程池配置
HystrixThreadPoolProperties.Setter thresholdConfig = HystrixThreadPoolProperties.Setter()
.withCoreSize(10) // 核心線程池大小
.withKeepAliveTimeMinutes(5) // 空閑線程存活時間
.withMaxQueueSize(Integer.MAX_VALUE) // 線程池隊列大小
.withQueueSizeRejectionThreshold(1000); //限定目前隊列大小
// 指令屬性配置,設定隔離政策為線程隔離
HystrixCommandProperties.Setter isolationStrategy = HystrixCommandProperties.Setter()
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD);
return HystrixCommand.Setter
.withGroupKey(hystrixCommandGroupKey)
.andCommandKey(commandKey)
.andThreadPoolKey(threadPoolKey)
.andThreadPoolPropertiesDefaults(thresholdConfig)
.andCommandPropertiesDefaults(isolationStrategy);
}
@Override
protected List run() throws Exception {
return problemService.findAll();
}
}
一些重要的元件分析:
HystrixCommandGroupKey: 配置全局唯一 辨別服務分組的名稱,比如,庫存系統就是一 個服務分組。當我們監控時,相同分組的服務會聚合在一 起,必填選項。
HystrixCommandKey: 配置全局唯一 辨別服務的名稱,比如,庫存系統有一 個擷取庫存服務,那麼就可以為這個服務起一 個名字來唯一 識别該服務,如果不配置,則預設是簡單類名。
HystrixThreadPoolKey: 配置全局唯一辨別線程池的名稱,相同線程池名稱的線程池是同一 個,如果不配置,則預設是分組名,此名字也是線程池中線程名字的字首。
HystrixThreadPoolProperties: 配置線程池參數,coreSize配置核心線程池大小和線程池最大大小,keepAliveTimeMinutes是線程池中空閑線程生存時間(如果不進行動态配置,那麼是沒有任何作用的),maxQueueSize配置線程池隊列最大大小,queueSizeRejectionThreshold限定目前隊列大小,即實際隊列大小由這個參數決定,通過改變queueSizeRejectionThreshold可以實作動态隊列大小調整。
HystrixCommandProperties: 配置該指令executionlsolationStrategy
配置執行隔離政策,預設是使用線程隔離,此處我們配置為THREAD, 即線程池隔離。
此處可以粗粒度實作隔離,也可以細粒度實作隔離.
服務分組+線程池:粗粒度實作,一個服務分組配置一個線程池,配置線程池名稱或者相同分組的線程池名稱配置為一 樣。
服務分組+服務+線程池:細粒度實作, 一個服務分組中的每一 個服務配置一 個隔離線程池,為不同的指令實作配置不同的線程池名稱即可。
混合實作:一個服務分組配置一個隔離線程池,然後對重要服務單獨設定隔離線程池。
在實際的應用中,如果要動态改變其中的一些參數,配置,則可以使用如下方式去動态修改:
例如要修改線程池配置:
String dynamicQueueSizeRej ectionThreshold = "hystrix. threadpool. " + "qa-pool"
+ ".queueSizeRejectionThreshold";
Configuration configuration = ConfigurationManager. getConfiginstance ();
configuration.setProperty(dynamicQueueSizeRejectionThreshold, 100);
同理:
如果是改變線程池配置,則是"hystrix.threadpool." + threadPoolKey + propertyName;
如果是改變指令屬性配置,則是"hystrix.command." + commandKey + propertyName。
調用時:
可以選擇同步調用或者異步調用。
還有一種是配置RxJava實作響應式程式設計,這裡不做過多概述。
信号量隔離
另外還有一個隔離方式是信号量隔離,就是在HystrixCommandProperties: 配置該指令executionlsolationStrategy
配置執行隔離政策如下:
信号量隔離隻是限制了總的并發數,服務使用主線程進行同步調用,即沒有線程池。是以,如果隻是想限制某個服務的總并發調用量或者調用的服務不涉及遠端調用的話,可以使用輕量級的信号量來實作。
待續。。。
喜歡可以關注我