Spring Cloud Hystrix實(shí)現(xiàn)了斷路器、線程隔離等一系列服務(wù)保護(hù)功能啤呼。它是基于Netflix的開(kāi)源框架Hystrix實(shí)現(xiàn)的局服,該框架的目標(biāo)在于通過(guò)控制那些訪問(wèn)遠(yuǎn)程系統(tǒng)磷仰、服務(wù)和第三方庫(kù)的節(jié)點(diǎn)优炬,從而對(duì)延遲和故障提供更強(qiáng)大的容錯(cuò)能力颁井。Hystrix具備服務(wù)降級(jí)(fallback)、服務(wù)熔斷蠢护、線程和信號(hào)隔離雅宾、請(qǐng)求緩存、請(qǐng)求合并以及服務(wù)監(jiān)控等強(qiáng)大功能葵硕。
What Is Hystrix For?
- 保護(hù)和控制對(duì)依賴項(xiàng)的網(wǎng)絡(luò)延時(shí)和失敗情況
- 防止級(jí)聯(lián)故障
- 快速失敗眉抬、快速恢復(fù)
- fallback和服務(wù)降級(jí)
- 接近實(shí)時(shí)級(jí)的監(jiān)控、報(bào)警和控制
How Does Hystrix Accomplish Its Goals?
- 使用命令模式將所有對(duì)外部服務(wù)(或依賴關(guān)系)的調(diào)用包裝在HystrixCommand或HystrixObservableCommand對(duì)象中懈凹,并將該對(duì)象放在單獨(dú)的線程中執(zhí)行蜀变;
- 支持自定義依賴項(xiàng)的超時(shí)時(shí)間,一般稍高于99.5%的訪問(wèn)時(shí)間介评;
- 每個(gè)依賴都維護(hù)著一個(gè)線程池(或信號(hào)量)库北,線程池被耗盡則拒絕請(qǐng)求(而不是讓請(qǐng)求排隊(duì))。
- 記錄請(qǐng)求成功们陆,失敗寒瓦,超時(shí)和線程拒絕。
- 服務(wù)錯(cuò)誤百分比超過(guò)了閾值坪仇,熔斷器開(kāi)關(guān)自動(dòng)打開(kāi)杂腰,一段時(shí)間內(nèi)停止對(duì)該服務(wù)的所有請(qǐng)求。
- 請(qǐng)求失敗烟很,被拒絕颈墅,超時(shí)或熔斷時(shí)執(zhí)行降級(jí)邏輯。
-
近實(shí)時(shí)地監(jiān)控指標(biāo)和配置的修改雾袱。
設(shè)計(jì)模式
命令模式
觀察者模式
艙壁模式
原理分析
工作流程
- 構(gòu)造一個(gè) HystrixCommand或HystrixObservableCommand對(duì)象恤筛,用于封裝請(qǐng)求,并在構(gòu)造方法配置請(qǐng)求被執(zhí)行需要的參數(shù)芹橡;
- 執(zhí)行命令毒坛,Hystrix提供了4種執(zhí)行命令的方法,后面詳述林说;
- 判斷是否使用緩存響應(yīng)請(qǐng)求煎殷,若啟用了緩存,且緩存可用腿箩,直接使用緩存響應(yīng)請(qǐng)求豪直。Hystrix支持請(qǐng)求緩存,但需要用戶自定義啟動(dòng)珠移;
- 判斷熔斷器是否打開(kāi)弓乙,如果打開(kāi)末融,跳到第8步;
- 判斷線程池/隊(duì)列/信號(hào)量是否已滿暇韧,已滿則跳到第8步勾习;
- 執(zhí)行HystrixObservableCommand.construct()或HystrixCommand.run(),如果執(zhí)行失敗或者超時(shí)懈玻,跳到第8步巧婶;否則,跳到第9步涂乌;
- 統(tǒng)計(jì)熔斷器監(jiān)控指標(biāo)艺栈;
- 走Fallback備用邏輯
- 返回請(qǐng)求響應(yīng)
執(zhí)行命令的幾種方法
Hystrix提供了4種執(zhí)行命令的方法,execute()和queue() 適用于HystrixCommand對(duì)象湾盒,而observe()和toObservable()適用于HystrixObservableCommand對(duì)象眼滤。
- execute()
以同步堵塞方式執(zhí)行run(),只支持接收一個(gè)值對(duì)象历涝。hystrix會(huì)從線程池中取一個(gè)線程來(lái)執(zhí)行run(),并等待返回值漾唉。
- queue()
以異步非阻塞方式執(zhí)行run()荧库,只支持接收一個(gè)值對(duì)象。調(diào)用queue()就直接返回一個(gè)Future對(duì)象赵刑》稚溃可通過(guò) Future.get()拿到run()的返回結(jié)果,但Future.get()是阻塞執(zhí)行的般此。若執(zhí)行成功蚪战,F(xiàn)uture.get()返回單個(gè)返回值。當(dāng)執(zhí)行失敗時(shí)铐懊,如果沒(méi)有重寫(xiě)fallback邀桑,F(xiàn)uture.get()拋出異常。
- observe()
事件注冊(cè)前執(zhí)行run()/construct()科乎,支持接收多個(gè)值對(duì)象壁畸,取決于發(fā)射源。調(diào)用observe()會(huì)返回一個(gè)hot Observable茅茂,也就是說(shuō)捏萍,調(diào)用observe()自動(dòng)觸發(fā)執(zhí)行run()/construct()若专,無(wú)論是否存在訂閱者唾戚。
如果繼承的是HystrixCommand,hystrix會(huì)從線程池中取一個(gè)線程以非阻塞方式執(zhí)行run()匀借;如果繼承的是HystrixObservableCommand碴倾,將以調(diào)用線程阻塞執(zhí)行construct()逗噩。
observe()使用方法:
- 調(diào)用observe()會(huì)返回一個(gè)Observable對(duì)象
- 調(diào)用這個(gè)Observable對(duì)象的subscribe()方法完成事件注冊(cè)掉丽,從而獲取結(jié)果
- toObservable()
事件注冊(cè)后執(zhí)行run()/construct(),支持接收多個(gè)值對(duì)象给赞,取決于發(fā)射源机打。調(diào)用toObservable()會(huì)返回一個(gè)cold Observable,也就是說(shuō)片迅,調(diào)用toObservable()不會(huì)立即觸發(fā)執(zhí)行run()/construct()残邀,必須有訂閱者訂閱Observable時(shí)才會(huì)執(zhí)行。
如果繼承的是HystrixCommand柑蛇,hystrix會(huì)從線程池中取一個(gè)線程以非阻塞方式執(zhí)行run()芥挣,調(diào)用線程不必等待run();如果繼承的是HystrixObservableCommand耻台,將以調(diào)用線程堵塞執(zhí)行construct()空免,調(diào)用線程需等待construct()執(zhí)行完才能繼續(xù)往下走。
toObservable()使用方法:
- 調(diào)用observe()會(huì)返回一個(gè)Observable對(duì)象
- 調(diào)用這個(gè)Observable對(duì)象的subscribe()方法完成事件注冊(cè)盆耽,從而獲取結(jié)果
需注意的是蹋砚,HystrixCommand也支持toObservable()和observe(),但是即使將HystrixCommand轉(zhuǎn)換成Observable摄杂,它也只能發(fā)射一個(gè)值對(duì)象坝咐。只有HystrixObservableCommand才支持發(fā)射多個(gè)值對(duì)象。
斷路器原理
依賴隔離
Hystrix為每個(gè)以來(lái)的服務(wù)創(chuàng)建一個(gè)獨(dú)立的線程池析恢,這樣就算某個(gè)依賴服務(wù)出現(xiàn)延遲過(guò)高的情況墨坚,也只是對(duì)該依賴服務(wù)的調(diào)用有影響,而不會(huì)拖慢其他依賴服務(wù)映挂。
另外Hystrix中除了可使用線程池之外泽篮,還可以使用信號(hào)量來(lái)控制單個(gè)以來(lái)服務(wù)的并發(fā)度,信號(hào)量的開(kāi)銷遠(yuǎn)比線程池的開(kāi)銷小柑船,但是它不能設(shè)置超時(shí)和實(shí)現(xiàn)異步訪問(wèn)帽撑。
使用詳解
構(gòu)建一個(gè)如下架構(gòu)圖的服務(wù)調(diào)用關(guān)系
分析上述架構(gòu)圖,主要有以下幾項(xiàng)工作:
- eureka-server工程: 服務(wù)注冊(cè)中心椎组,端口1111
- hello-service工程: HELLO-SERVICE服務(wù)單元油狂,啟動(dòng)兩個(gè)實(shí)例,端口分別為8081和8082
- ribbon-consumer工程: 使用Ribbon實(shí)現(xiàn)的服務(wù)消費(fèi)者寸癌,端口9000
下面我們開(kāi)始引入Spring Cloud Hystrix专筷。
- 在ribbeon-consumer工程中引入依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
- 在ribbon-consumer工程主類ConsumerApplication中使用@Enable-CircuitBreaker注釋開(kāi)啟斷路器功能:
@EnableCircuitBreaker
@EnableDiscoveryClient
@SpringBootApplication
public class ConsumerApplication {
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main (String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
HystrixCommand支持繼承和注釋兩種寫(xiě)法,本文只介紹注釋方法蒸苇,對(duì)繼承方法感興趣的同學(xué)可以baidu一些資料磷蛹。
- 同步執(zhí)行
public class UserService {
@Autowired
private RestTemplate restTemplate;
@HystrixCommand
public User getUserById(Long id) {
return restTemplate.getForObject("http://USER-SERVICE/users/{1}", User.class, id);
}
}
- 異步執(zhí)行
public class UserService {
@Autowired
private RestTemplate restTemplate;
@HystrixCommand
public Future<User> getUserByIdAsync(final String id) {
return new AsyncResult<User>() {
@Override
public User invoke() {
return restTemplate.getForObject("http://USER-SERVICE/users/{1}", User.class, id);
}
}
}
}
除了傳統(tǒng)的同步執(zhí)行與異步執(zhí)行之外,我們還可以將HystrixCommand通過(guò)Observale來(lái)實(shí)現(xiàn)響應(yīng)式執(zhí)行方式溪烤。
public class UserService {
@Autowired
private RestTemplate restTemplate;
@HystrixCommand(observableExecutionMode = ObservableExecutionMode.EAGER)
public Observable<String> getUserById(final String id) {
return Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> observer) {
try {
if (!observer.isUnsubscribed()) {
String user = restTemplate.getForObject("http://USER-SERVICE/users/{1}", String.class, id);
observer.onNext(user);
observer.onCompleted();
}
} catch (Exception e) {
observer.onError(e);
}
}
});
}
}
下面是一個(gè)簡(jiǎn)單的Observable的使用示例味咳。關(guān)于響應(yīng)式編程庇勃,有興趣的可以研究一下RxJava。
public void useObservable () {
Observable<String> observable = userService.getUserById("1");
observable.subscribe(new Action1<String>() {
@Override
public void call(String s) {
//
}
});
}
- 定義服務(wù)降級(jí)
fallback是Hystrix命令執(zhí)行失敗時(shí)使用的后備方法槽驶,用來(lái)實(shí)現(xiàn)服務(wù)的降級(jí)處理邏輯责嚷。在HystrixCommand中可以通過(guò)重載getFallBack()方法來(lái)實(shí)現(xiàn)服務(wù)降級(jí)邏輯,Hystrix會(huì)再run()執(zhí)行過(guò)程中出現(xiàn)錯(cuò)誤掂铐、超時(shí)罕拂、線程池拒絕、斷路器熔斷等情況全陨,執(zhí)行g(shù)etFallback()方法內(nèi)的邏輯爆班。
public class UserService {
@Autowired
private RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "defaultUser")
public String getUserById(Long id) {
return restTemplate.getForObject("http://USER-SERVICE/users/{1}", String.class, id);
}
public String defaultUser() {
return "default user";
}
}
如果fallback邏輯還是不穩(wěn)定,可以添加多級(jí)fallback邏輯辱姨。
public class UserService {
@Autowired
private RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "defaultUser")
public String getUserById(Long id) {
return restTemplate.getForObject("http://USER-SERVICE/users/{1}", String.class, id);
}
@HystrixCommand(fallbackMethod = "defaultUserSec")
public String defaultUser() {
return "first default user";
}
public String defaultUserSec() {
return "second default user";
}
}
不論Hystrix命令是否實(shí)現(xiàn)了服務(wù)降級(jí)柿菩,命令狀態(tài)和斷路器狀態(tài)都會(huì)更新,并且我們可以由此了解到命令執(zhí)行的失敗情況雨涛。
- 忽略異常
public class UserService {
@Autowired
private RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "defaultUser", ignoreExceptions = {HystrixBadRequestException.class})
public String getUserById(Long id) {
return restTemplate.getForObject("http://USER-SERVICE/users/{1}", String.class, id);
}
public String defaultUser(Long id, Throwable e) {
return "default user";
}
}
- 命名名稱枢舶、分組以及線程池劃分
@HystrixCommand(commandKey = "getUserById", groupKey = "UserGroup", threadPoolKey = "getUserByIdThread")
public String getUserById(Long id) {
return restTemplate.getForObject("http://USER-SERVICE/users/{1}", String.class, id);
}
- 請(qǐng)求緩存
@CacheResult
@HystrixCommand
public String getUserById(@CacheKey("id") Long id) {
return restTemplate.getForObject("http://USER-SERVICE/users/{1}", String.class, id);
}
@CacheRemove(commandKey = "getUserById")
@HystrixCommand
public void update(@CacheKey("id") User user) {
return restTemplate.postForObject("http://USER-SERVICE/users/{1}", user, User.class);
}
- 請(qǐng)求合并
HystrixCollapser可以實(shí)現(xiàn)對(duì)以來(lái)服務(wù)請(qǐng)求的合并,以減少通信小號(hào)和線程數(shù)的占用替久。
@Service
public class UserService {
@Autowired
private RestTemplate restTemplate;
@HystrixCollapser(batchMethod = "findAll", collapserProperties = {
@HystrixProperty(name = "timeDelayInMilliseconds", value = "100")
})
public String find(Long id) {
return null;
}
@HystrixCommand
public List<String> findAll(List<Long> ids) {
return restTemplate.getForObject("http://USER-SERVICE/users?ids={1}", List.class,
StringUtils.join(ids, ","));
}
}
屬性詳解
Hystrix儀表盤(pán)
Turbine集群監(jiān)控
Reference
Netflix/Hystrix Wiki
Netflix/Hystrix Wiki How-To-Use
Hystrix原理與實(shí)戰(zhàn)