在分布式環(huán)境中反镇,許多服務(wù)依賴項中的一些必然會失敗。Hystrix是一個庫,通過添加延遲容忍和容錯邏輯张足,幫助你控制這些分布式服務(wù)之間的交互。Hystrix通過隔離服務(wù)之間的訪問點(diǎn)坎藐、停止級聯(lián)失敗和提供回退選項來實現(xiàn)這一點(diǎn)为牍,所有這些都可以提高系統(tǒng)的整體彈性
一、Hystrix宗旨和工作原理
Hystrix的宗旨:
- 防止任何單個依賴項耗盡所有容器(如Tomcat)用戶線程
- 在任何可行的地方提供回退岩馍,以保護(hù)用戶不受失敗的影響
- 使用隔離技術(shù)(如隔離板碉咆、泳道和斷路器模式)來限制任何一個依賴項的影響
- 通過近實時的度量、監(jiān)視和警報來優(yōu)化發(fā)現(xiàn)時間
- 通過配置的低延遲傳播來優(yōu)化恢復(fù)時間
- 避免在整個依賴客戶端執(zhí)行中出現(xiàn)故障蛀恩,而不僅僅是在網(wǎng)絡(luò)流量中
Hystrix工作原理
當(dāng)需要完成某項任務(wù)時疫铜,通過 Hystrix 將任務(wù)包裹起來,交由 Hystrix 來完成任務(wù)双谆,從而享受 Hystrix 帶來保護(hù)
- 1壳咕、構(gòu)建命令
Hystrix 提供了兩個Command席揽, HystrixCommand 和 HystrixObservableCommand,可以使用這兩個對象來包裹待執(zhí)行的任務(wù),Hystrix應(yīng)用自己的一系列保護(hù)機(jī)制谓厘,在執(zhí)行用戶任務(wù)的各節(jié)點(diǎn)(執(zhí)行前幌羞、執(zhí)行后、異常竟稳、超時等)做一系列的事情
-
2属桦、執(zhí)行命令
有四種方式執(zhí)行command
- R execute():同步執(zhí)行,從依賴服務(wù)得到單一結(jié)果對象
- Future<R> queue():異步執(zhí)行他爸,返回一個 Future 以便獲取執(zhí)行結(jié)果聂宾,也是單一結(jié)果對象
- Observable<R> observe():hot observable,創(chuàng)建Observable后會訂閱Observable诊笤,可以返回多個結(jié)果
- Observable<R> toObservable():cold observable系谐,返回一個Observable,只有訂閱時才會執(zhí)行盏混,可以返回多個結(jié)果
- 3揣炕、檢查緩存
如果啟用了 Hystrix Cache帐我,任務(wù)執(zhí)行前將先判斷是否有相同命令執(zhí)行的緩存拿诸。如果有則直接返回緩存的結(jié)果擦囊;如果沒有緩存的結(jié)果,但啟動了緩存混聊,將緩存本次執(zhí)行結(jié)果以供后續(xù)使用
- 4弹谁、檢查斷路器是否打開
斷路器(circuit-breaker)和保險絲類似,保險絲在發(fā)生危險時將會燒斷以保護(hù)電路句喜,而斷路器可以在達(dá)到我們設(shè)定的閥值時觸發(fā)短路(比如請求失敗率達(dá)到50%)预愤,拒絕執(zhí)行任何請求。如果斷路器被打開咳胃,Hystrix 將不會執(zhí)行命令植康,直接進(jìn)入Fallback處理邏輯
- 5、檢查線程池/信號量情況
Hystrix 隔離方式有線程池隔離和信號量隔離展懈。當(dāng)使用Hystrix線程池時销睁,Hystrix 默認(rèn)為每個依賴服務(wù)分配10個線程,當(dāng)10個線程都繁忙時存崖,將拒絕執(zhí)行命令冻记。信號量同理
- 6、執(zhí)行具體的任務(wù)
通過
HystrixObservableCommand.construct()
或者HystrixCommand.run()
來運(yùn)行用戶真正的任務(wù)
- 7来惧、計算鏈路健康情況
每次開始執(zhí)行command冗栗、結(jié)束執(zhí)行command以及發(fā)生異常等情況時,都會記錄執(zhí)行情況,例如:成功隅居、失敗钠至、拒絕以及超時等情況,會定期處理這些數(shù)據(jù)胎源,再根據(jù)設(shè)定的條件來判斷是否開啟斷路器
- 8棕洋、命令失敗時執(zhí)行 Fallback 邏輯
在命令失敗時執(zhí)行用戶指定的 Fallback 邏輯。上圖中的斷路乒融、線程池拒絕、信號量拒絕摄悯、執(zhí)行執(zhí)行赞季、執(zhí)行超時都會進(jìn)入 Fallback 處理
- 9、返回執(zhí)行結(jié)果
原始結(jié)果將以O(shè)bservable形式返回奢驯,在返回給用戶之前申钩,會根據(jù)調(diào)用方式的不同做一些處理
更多詳細(xì)的工作原理,可前往: How is Works
二瘪阁、HystrixCommand注解
我們的配置都是基于 HystrixCommand 的撒遣,我們通過在方法上添加 @HystrixCommand 注解并配置注解的參數(shù)來實現(xiàn)配置,但有的時候一個類里面會有多個 Hystrix 方法管跺,每個方法都是類似配置的話會冗余很多代碼义黎,這時候我們可以在類上使用 @DefaultProperties 注解來給整個類的 Hystrix 方法設(shè)置一個默認(rèn)值。
參數(shù)介紹
- commandKey: 用來標(biāo)識一個 Hystrix 命令豁跑,默認(rèn)會取被注解的方法名廉涕。需要注意:Hystrix 里同一個鍵的唯一標(biāo)識并不包括 groupKey,建議取一個獨(dú)一二無的名字艇拍,防止多個方法之間因為鍵重復(fù)而互相影響
- groupKey:一組Hystrix命令的集合狐蜕,統(tǒng)計、報告卸夕、默認(rèn)取類名层释,可不配置
- threadPoolKey:標(biāo)識一個線程池,如果沒設(shè)置會取groupKey快集,很多情況下都是同一個類內(nèi)的方法共用一個線程池
- commandProperties:與此命令相關(guān)的屬性
- threadPoolProperties:與線程池相關(guān)的屬性
-
observableExcutionMode:當(dāng)Hystrix命令被包裝成RxJava的Observer異步執(zhí)行時贡羔,此配置指定了Observable被執(zhí)行的模式,默認(rèn)是
ObservableExecutionMode.EAGER
, Obserable會在被創(chuàng)建后立刻執(zhí)行碍讨,而ObservableExecutionMode.EAGER
模式下治力,則會產(chǎn)生一個Observable被Subscribe執(zhí)行,此配置項可以不配置 - ignoreExceptions:默認(rèn)Hystrix在執(zhí)行方法時捕獲到異常時執(zhí)行回退勃黍,并統(tǒng)計失敗率以修改熔斷器的狀態(tài)宵统,而被忽略的異常則會直接拋到外層,不執(zhí)行回退,也不影響熔斷器的狀態(tài)
- raiseHystrixExceptions:當(dāng)配置項包括 HystrixRuntimeException 時马澈,所有的未被忽略的異常都會被包裝成HystrixRuntimeException瓢省,配置其他種類的異常好像并沒有什么影響
-
fallbackMethod:方法執(zhí)行時容斷、錯誤痊班、超時會執(zhí)行的回退方法勤婚,需要
保證返回值一致
三、hystrix配置屬性詳解
Hystrix配置屬性詳解涤伐,所有屬性默認(rèn)都是default,如需針對某個服務(wù)馒胆,將default改為那個服務(wù)名即可
- Execution:控制HystrixCommand.run()如何運(yùn)行
- Fallback:控制HystrixCommand.getFallback()如何運(yùn)行
- Circuit Breadker:控制斷路器的行為
- Metrics:捕獲HystrixCommand和HystrixObervableCommand執(zhí)行消息相關(guān)的配置屬性
- Request Context:設(shè)置請求上下文的屬性
- Collapser Properties:設(shè)置請求合并的屬性
- Thread Pool Properties:設(shè)置線程池的屬性
1、Execution
-
execution.isolation.strategy:表示Hystrix.command.run()執(zhí)行時的隔離策略凝果,有以下兩種
- Thread:在單獨(dú)的線程上執(zhí)行祝迂,并發(fā)請求受線程池中的線程數(shù)限制
- SEMAPHORE:在調(diào)用線程上執(zhí)行,并發(fā)請求量受信號量計數(shù)限制
在默認(rèn)情況下器净,推薦HystrixCommands 使用 thread 隔離策略型雳,HystrixObservableCommand 使用 semaphore 隔離策略。 只有在高并發(fā)(單個實例每秒達(dá)到幾百個調(diào)用)的調(diào)用時山害,才需要修改HystrixCommands 的隔離策略為semaphore 纠俭。semaphore 隔離策略通常只用于非網(wǎng)絡(luò)調(diào)用
hystrix.command.default.execution.isolation.strategy=
- execution.isolation.thread.timeoutInMilliseconds:設(shè)置調(diào)用者執(zhí)行的超時實際(單位毫秒),默認(rèn)1000
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=
- execution.isolation.thread.interruptOnTimeout:表示設(shè)置執(zhí)行超時時浪慌,中斷HystrixCommand.run() 的執(zhí)行冤荆,默認(rèn)true
hystrix.command.default.execution.isolation.thread.interruptOnTimeout=
- execution.isolation.semaphore.maxConcurrentRequests:當(dāng)HystrixCommand.run()使用SEMAPHORE隔離策略時,設(shè)置并發(fā)量权纤,默認(rèn)10
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests=
- execution.isolation.thread.interruptOnCancel:表示設(shè)置是否在取消任務(wù)執(zhí)行時匙赞,中斷HystrixCommand.run()執(zhí)行,默認(rèn)false
hystrix.command.default.execution.isolation.thread.interruptOnCancel=
2妖碉、Fallback
以下屬性控制HystrixCommand.getFallback() 如何執(zhí)行涌庭。這些屬性對隔離策略THREAD 和SEMAPHORE都起作用
- fallback.isolation.semaphore.maxConcurrentRequests:此屬性設(shè)置從調(diào)用線程允許HystrixCommand.getFallback()方法允許的最大并發(fā)請求數(shù) 如果達(dá)到最大的并發(fā)量,則接下來的請求會被拒絕并且拋出異常欧宜,默認(rèn)10
hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests=
- fallback.enabled:是否開啟fallback功能坐榆,默認(rèn)true
hystrix.command.default.fallback.enabled=
3、Circuit Breaker
控制斷路器的行為
- circuitBreaker.enabled:是否開啟斷路器功能冗茸,默認(rèn)true
hystrix.command.default.circuitBreaker.enabled=
- circuitBreaker.requestVolumeThreshold:使斷路器跳閘的最小請求數(shù)量席镀,如果此屬性值為20,則在窗口時間內(nèi)(如10s內(nèi))夏漱,如果只收到19個請求且都失敗了豪诲,則斷路器也不會開啟。默認(rèn)20
hystrix.command.default.circuitBreaker.requestVolumeThreshold=
- circuitBreaker.errorThresholdPercentage:設(shè)置失敗百分比的閾值挂绰。如果失敗比率超過這個值屎篱,則斷路器跳閘并且進(jìn)入fallback邏輯,默認(rèn)50
hystrix.command.default.circuitBreaker=
- circuitBreaker.forceOpen:如果設(shè)置true,則強(qiáng)制使斷路器跳閘交播,則會拒絕所有的請求.此值會覆蓋circuitBreaker.forceClosed的值重虑,默認(rèn)false
hystrix.command.default.circuitBreaker.forceOpen=
- circuitBreaker.forceClosed:如果設(shè)置true,則強(qiáng)制使斷路器進(jìn)行關(guān)閉狀態(tài)秦士,此時會允許執(zhí)行所有請求缺厉,無論是否失敗的次數(shù)達(dá)到circuitBreaker.errorThresholdPercentage值,默認(rèn)false
hystrix.command.default.circuitBreaker.forceClosed=
4隧土、Mertrics
捕獲和HystrixCommand 和 HystrixObservableCommand 執(zhí)行信息相關(guān)的配置屬性
- metrics.rollingStats.timeInMilliseconds:設(shè)置統(tǒng)計滾動窗口的時間長度如果此值為10s提针,將窗口分成10個桶,每個桶表示1s時間曹傀,默認(rèn)10000
hystrix.command.default.metrics.rollingStats.timeInMilliseconds=
-
metrics.rollingStats.numBuckets:設(shè)置統(tǒng)計滾動窗口的桶數(shù)量关贵,
必須滿足metrics.rollingStats.timeInMilliseconds % metrics.rollingStats.numBuckets == 0
,否則異常卖毁,比如:1000/10,1000/20是正確的落萎,1000/7是錯的亥啦,高并發(fā)環(huán)境里,每個桶時間長度建議大于100ms
练链,默認(rèn)10
hystrix.command.default.metrics.rollingStats.numBuckets=
- metrics.rollingPercentile.enabled:設(shè)置執(zhí)行延遲是否被跟蹤翔脱,并且被計算在失敗百分比中。如果設(shè)置為false,則所有的統(tǒng)計數(shù)據(jù)返回-1媒鼓,默認(rèn)true
hystrix.command.default.metrics.rollingPercentile.enabled=
- metrics.rollingPercentile.timeInMilliseconds:此屬性設(shè)置統(tǒng)計滾動百分比窗口的持續(xù)時間届吁,默認(rèn)60000
hystrix.command.default.metrics.rollingPercentile.timeInMilliseconds=
-
metrics.rollingPercentile.numBuckets:設(shè)置統(tǒng)計滾動百分比窗口的桶數(shù)量,以下配置必須成立绿鸣,
metrics.rollingPercentile.timeInMilliseconds % metrics.rollingPercentile.numBuckets == 0
疚沐,否則異常,比如1000/10是對的潮模,1000/7是錯的亮蛔,高并發(fā)情況下每個桶的時間長度建議大于1000ms
,默認(rèn)6
hystrix.command.default.metrics.rollingPercentile.numBuckets=
- metrics.rollingPercentile.bucketSize:設(shè)置每個桶保存的執(zhí)行時間的最大值擎厢。如果桶數(shù)量是100究流,統(tǒng)計窗口為10s,如果這10s里有500次執(zhí)行动遭,只有最后100次執(zhí)行會被統(tǒng)計到bucket里去芬探,默認(rèn)100
hystrix.command.default.metrics.rollingPercentile.bucketSize=
- metrics.healthSnapshot.intervalInMilliseconds:采樣時間間隔
hystrix.command.default.metrics.healthSnapshot.intervalInMilliseconds=
5、Request Context
此屬性控制HystrixCommand使用到的Hystrix的上下文
- requestCache.enabled:是否開啟請求緩存功能厘惦,默認(rèn)true
hystrix.command.default.requestCache.enabled=
- requestLog.enabled:是否開啟日志偷仿,打印執(zhí)行HystrixCommand的情況和事件,默認(rèn)true
hystrix.command.default.requestLog.enabled=
6、Collapser Properties
設(shè)置請求合并的屬性
- maxRequestsInBatch:設(shè)置同時批量執(zhí)行的請求的最大數(shù)量炎疆,默認(rèn)Integer.MAX_VALUE
hystrix.collapser.default.maxRequestsInBatch=
- timerDelayInMilliseconds:批量執(zhí)行創(chuàng)建多久之后卡骂,再觸發(fā)真正的請求,默認(rèn)10
hystrix.collapser.default.timerDelayInMilliseconds=
- requestCache.enabled:是否對HystrixCollapser.execute() 和 HystrixCollapser.queue()開啟請求緩存,默認(rèn)true
hystrix.collapser.default.requestCache.enabled=
7形入、Thread Pool Properties
設(shè)置Hystrix Commands的線程池行為全跨,大部分情況線程數(shù)量是10。線程池數(shù)量的計算公式如下:最高峰時每秒的請求數(shù)量 × 99%命令執(zhí)行時間 + 喘息空間
亿遂,設(shè)置線程池數(shù)量的主要原則是保持線程池越小越好
浓若,因為它是減輕負(fù)載并防止資源在延遲發(fā)生時被阻塞的主要工具
- coreSize:設(shè)置線程池的core的大小,默認(rèn)10
hystrix.threadpool.default.coreSize=
- maximumSize:設(shè)置最大的線程池的大小蛇数,只有設(shè)置allowMaximumSizeToDivergeFromCoreSize時挪钓,此值才起作用,默認(rèn)10
hystrix.threadpool.default.maximumSize=
- maxQueueSize:設(shè)置最大的BlockingQueue隊列的值耳舅。如果設(shè)置-1碌上,則使用SynchronousQueue隊列,如果設(shè)置正數(shù)浦徊,則使用LinkedBlockingQueue隊列馏予,默認(rèn)-1
hystrix.threadpool.default.maxQueueSize=
- queueSizeRejectionThreshold:因為maxQueueSize值不能被動態(tài)修改,所有通過設(shè)置此值可以實現(xiàn)動態(tài)修改等待隊列長度盔性。即等待的隊列的數(shù)量大于queueSizeRejectionThreshold時(但是沒有達(dá)到maxQueueSize值)霞丧,則開始拒絕后續(xù)的請求進(jìn)入隊列。如果設(shè)置-1冕香,則屬性不啟作用蛹尝,默認(rèn)5
hystrix.threadpool.default.queueSizeRejectionThreshold=
- keepAliveTimeMinutes:設(shè)置線程多久沒有服務(wù)后,需要釋放(maximumSize-coreSize )個線程悉尾,默認(rèn)1
hystrix.threadpool.default.keepAliveTimeMinutes=
- allowMaximumSizeToDivergeFromCoreSize:設(shè)置allowMaximumSizeToDivergeFromCoreSize值為true時突那,maximumSize才有作用,默認(rèn)false
hystrix.threadpool.default.allowMaximumSizeToDivergeFromCoreSize=
- metrics.rollingStats.timeInMilliseconds:設(shè)置滾動窗口的時間构眯,默認(rèn)10000
hystrix.threadpool.default.metrics.rollingStats.timeInMilliseconds=
-
metrics.rollingStats.numBuckets:設(shè)置滾動靜態(tài)窗口分成的桶的數(shù)量陨收,必須滿足
metrics.rollingStats.timeInMilliseconds % metrics.rollingStats.numBuckets == 0
,默認(rèn)值10鸵赖,高并發(fā)情況下建議每個桶時間長度大于100ms
hystrix.threadpool.default.metrics.rollingStats.numBuckets=
四务漩、Hystrix的使用
1、導(dǎo)入依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>cn.gjing</groupId>
<artifactId>tools-httpclient</artifactId>
<version>1.0.2</version>
</dependency>
2它褪、啟動類標(biāo)注注解
/**
* @author Gjing
*/
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker
public class HystrixApplication {
public static void main(String[] args) {
SpringApplication.run(HystrixApplication.class, args);
}
}
3饵骨、編寫service
@Service
public class TestService {
// 此注解說明該方法要執(zhí)行回退,fallback指定回退方法
@HystrixCommand(fallbackMethod = "defaultFallback")
public String hello() {
HttpClient httpClient = new HttpClient();
return httpClient.get("http://127.0.0.1:8090/test2", String.class);
}
// 回退方法
public String defaultFallback() {
return "no hello";
}
}
4茫打、編寫Controller進(jìn)行測試
/**
* @author Gjing
**/
@RestController
public class TestController {
@Resource
private TestService testService;
@PostMapping("/test")
public String test() {
return testService.hello();
}
}
5居触、目標(biāo)服務(wù)的接口
因為hystrix默認(rèn)超時是1秒妖混,所以,我們在目標(biāo)服務(wù)轮洋,加個線程休眠制市,大于1秒即可
/**
* @author Gjing
**/
@RestController
public class DemoController {
@GetMapping("/test2")
public String test2() throws InterruptedException {
Thread.sleep(1000);
return "ok";
}
}
5、測試
超時進(jìn)入回退方法弊予,并返回了回退方法里的內(nèi)容
三祥楣、Feign使用Hystrix
1、增加依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
2汉柒、啟動類增加@EnableFeignClients
注解
3误褪、增加一個service和回退類
/**
* @author Gjing
* name為目標(biāo)服務(wù)名,fallback為回退類
**/
@FeignClient(name = "demo",fallback = FeignServiceFallbackImpl.class)
public interface FeignService {
@RequestMapping(value = "/test2", method = RequestMethod.GET)
String test2();
}
/**
* 回退類碾褂,實現(xiàn)feignService
*/
@Component
class FeignServiceFallbackImpl implements FeignService{
@Override
public String test2() {
return "啊哦兽间,出錯了");
}
}
4、編寫Controller進(jìn)行測試
/**
* @author Gjing
**/
@RestController
public class FeignController {
@Resource
private FeignService feignService;
@PostMapping("/testFeign")
public String testFeign() {
return feignService.test();
}
}
如果出錯了正塌,會進(jìn)行回退
四嘀略、使用監(jiān)控面板dashBoard
1、增加依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
2乓诽、啟動類加上注解@EnableHystrixDashboard
3帜羊、配置文件
server:
port: 8082
spring:
application:
name: hystrix-demo
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
# feign使用hystrix進(jìn)行回退
feign:
hystrix:
enabled: true
# 端點(diǎn)管理 hystrixDashboard
management:
endpoints:
web:
exposure:
include: "*"
4、啟動項目问裕,并訪問http://localhost:port/hystrix即可打開如下頁面
通過主頁面的文字介紹,可以知道孵坚,共支持三種不同的監(jiān)控方式*
- 默認(rèn)的集群監(jiān)控粮宛,通過URL:http://turbine-hostname:port/turbine.stream開啟監(jiān)控
- 指定的集群監(jiān)控,通過URL:http://turbine-hostname:port/turbine.stream?cluster=[clusterName]開啟監(jiān)控
- 單體應(yīng)用的監(jiān)控卖宠,通過URL:http://hystrix-app:port/actuator/hystrix.stream開啟監(jiān)控
本文只講解單體應(yīng)用巍杈,因此我們在主頁面輸入http://localhost:8082/actuator/hystrix.stream即可訪問,請求幾次結(jié)果
界面解釋
到此本文就結(jié)束了,篇幅較長扛伍,如哪里有字寫錯或單詞打錯筷畦,各位幫忙糾正,本項目源碼:SpringCloud-Demo