白話SpringCloud | 第五章:服務容錯保護(Hystrix)
前言
前一章節,我們知道了如何利用
RestTemplate
+Ribbon
和Feign
的方式進行服務的呼叫。在微服務架構中,一個服務可能會呼叫很多的其他微服務應用,雖然做了多叢集部署,但可能還會存在諸如網路原因或者服務提供者自身處理的原因,或多或少都會出現請求失敗或者請求延遲問題,若服務提供者長期未對請求做出迴應,服務消費者又不斷的請求下,可能就會造成服務提供者服務崩潰,進而服務消費者也一起跟著不可用,嚴重的時候就發生了系統雪崩
了。鑑於此,產生了斷路器等一系列的服務保護機制。本章節,就來說下如何利用Hystrix
進行容錯處理。
一點知識
按照此係列的慣例,我們先來了解下一些相關的知識。
注:以下部分內容轉至大佬純潔的微笑:
容錯處理手段
容錯處理是指軟體執行時,能對由非正常因素引起的執行錯誤給出適當的處理或資訊提示,使軟體執行正常結束——百度百科
從百度百科的解釋中可以看出,簡單理解,所謂的容錯處理
其實就是捕獲異常了,不讓異常影響系統的正常執行,正如java
中的try catch
一樣。
而在微服務呼叫中,自身異常可自行處理外,對於依賴的服務若發生錯誤,或者呼叫異常,或者呼叫時間過長等原因時,避免長時間等待,造成系統資源耗盡。
一般上都會通過設定請求的超時時間
,如http
請求中的ConnectTimeout
和ReadTimeout
;再或者就是使用熔斷器
模式,隔離問題服務,防止級聯錯誤等。
雪崩效應
在微服務架構中,存在很多的微服務單元,各個微服務之間通過網路進行通訊,難免出現依賴關係,若某一個單元
出現故障
,就很容易因依賴關係而引發故障的蔓延,產生“雪崩效應”
,最終導致整個系統的癱瘓。
下面這張圖,相比大家都有看過了。
如圖所示:A作為服務提供者,B為A的服務消費者,C和D是B的服務消費者。A不可用引起了B的不可用,並將不可用像滾雪球一樣放大到C和D時,雪崩效應就形成了。也就應了那句話:星星之火,可以燎原!
熔斷器
熔斷器,和現實生活中的空氣開關
作用很像。它可以實現快速失敗,如果它在一段時間內偵測到許多類似的錯誤,會強迫其以後的多個呼叫快速失敗,不再訪問遠端伺服器,從而防止應用程式不斷地嘗試執行可能會失敗的操作
熔斷器模式就像是那些容易導致錯誤的操作的一種代理。這種代理能夠記錄最近呼叫發生錯誤的次數,然後決定使用允許操作繼續,或者立即返回錯誤。
可以看出,熔斷器一共有三種狀態,之間轉換關係如下:
- 關閉狀態 當熔斷器處於關閉狀態時,請求是可以被放行的; 當熔斷器統計的失敗次數觸發開關時,轉為開啟狀態。
- 開啟狀態 當熔斷器處於開啟狀態時,所有請求都是不被放行的,直接返回失敗; 只有在經過一個設定的時間視窗週期後,熔斷器才會轉換到半開狀態
- 半開狀態 當熔斷器處於半開狀態時,當前只能有一個請求被放行; 這個被放行的請求獲得遠端服務的響應後,假如是成功的,熔斷器轉換為關閉狀態,否則轉換到開啟狀態。
個人覺得,主要還是快速失敗,避免請求堆積,壓垮伺服器。進而起到保護服務高可用的目的。
Hystrix實踐
何為Hystrix
Hystrix是一個實現了超時機制和斷路器模式的工具類庫。
Hystrix是有Netflix開源的一個延遲和容錯庫,用於隔離訪問遠端系統、服務或第三方庫,防止級聯失敗,從而提升系統的可用性和容錯性。
Hystrix容錯機制:
- 包裹請求:使用HystrixCommand包裹對依賴的呼叫邏輯,每個命令在獨立執行緒中執行,這是用到了設計模式“命令模式”。
- 跳閘機制:當某服務的錯誤率超過一定閾值時,Hystrix可以自動或手動跳閘,停止請求該服務一段時間。
- 資源隔離:Hystrix為每個依賴都維護了一個小型的執行緒池,如果該執行緒池已滿,發往該依賴的請求就被立即拒絕,而不是排隊等候,從而加速判定失敗。
- 監控:Hystrix可以近乎實時的監控執行指標和配置的變化。如成功、失敗、超時、被拒絕的請求等。
- 回退機制:當請求失敗、超時、被拒絕,或當斷路器開啟時,執行回退邏輯。回退邏輯可自定義。
- 自我修復:斷路器開啟一段時間後,會自動進入半開狀態,斷路器開啟、關閉、半開的邏輯轉換。
下圖就是Hystrix
的回退策略,防止級聯故障。
常規方式整合Hystrix
建立個工程spring-cloud-hystrix
工程。
0.引入POM依賴。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
1.啟動類,加入註解@EnableHystrix
,同時申明一個實現負載均衡的RestTemplate
。(關於消費者服務可檢視:第四章:服務消費者(RestTemple+Ribbon+Feign),這裡不再闡述了。)
/**
* 熔斷器示例
* @author oKong
*
*/
@SpringBootApplication
@EnableHystrix
@EnableDiscoveryClient
@Slf4j
public class HystrixApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(HystrixApplication.class, args);
log.info("sprign-cloud-hystrix啟動!");
}
@Bean
@LoadBalanced
public RestTemplate restTemplat() {
return new RestTemplate();
}
}
2.編寫一個測試類,加入@HystrixCommand
,指定fallbackMethod
方法。
RibbonController.java
/**
* ribbon 常規方式-示例
* @author oKong
*
*/
@RestController
@Slf4j
public class RibbonController {
@Autowired
RestTemplate restTemplate;
@GetMapping("/ribbon")
@HystrixCommand(fallbackMethod="fallback")
public String hello(String name) {
log.info("使用restTemplate呼叫服務,引數name:{}", name);
return restTemplate.getForObject("http://eureka-client/hello?name=" + name, String.class);
}
/**
* 發生熔斷時呼叫的方法
* @param name
* @param throwable 發生異常時的異常資訊
* @return
*/
public String fallback(String name,Throwable throwable) {
log.error("熔斷髮生了:{}", throwable);
log.warn("restTemplate呼叫服務發生熔斷,引數name:{}", name);
return "restTemplate呼叫服務發生熔斷,引數name:" + name;
}
}
注意:這裡fallback
方法加入了一個引數throwable
,當發生熔斷時,可以獲悉發生熔斷的異常資訊,便於定位問題和原因。
3.啟動應用,訪問:http://127.0.0.1:8038/ribbon?name=oKong 。正常情況下,spring-cloud-eureka-client
應用正常執行時,返回正常結果:
現在我們停止提供者服務,再次訪問,可以看見已經進入熔斷方法了:
控制檯可以看見異常輸出:
由於例項尚未被剔除註冊中心的服務列表,所以提示是連線超時,等待一段時間後,再次訪問服務,可以看見是提示例項不存在了:
注意:對於@HystrixCommand
註解,我們可以放在任何一個呼叫函式裡面,以此實現呼叫方法發生異常或者錯誤時,可以快速返回,避免持續請求,造成資源的耗盡。
Feign整合Hystrix
如上小節說示例的,當我們方法很多時,要是分別編寫一個fallback
估計也是崩潰的,雖然可以使用一個通用的fallback
,但未進行特殊設定下,也是無法知道具體是哪個方法發生熔斷的。
而對於Feign
,我們可以使用一種更加優雅的形式進行。我們可以指定@FeignClient
註解的fallback
屬性,或者是fallbackFactory
屬性,後者可以獲取異常資訊的。
修改spring-cloud-hystrix
工程。
0.引入Feigin
的POM依賴。
<!-- feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
1.啟動類,加入@EnableFeignClients
啟用Feign
.
**
* 熔斷器示例
* @author oKong
*
*/
@SpringBootApplication
@EnableHystrix
@EnableDiscoveryClient
@EnableFeignClients
@Slf4j
public class HystrixApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(HystrixApplication.class, args);
log.info("sprign-cloud-hystrix啟動!");
}
@Bean
@LoadBalanced
public RestTemplate restTemplat() {
return new RestTemplate();
}
}
- 建立一個服務介面類
IHelloClient.java
,同時定義fallback
或者fallbackFactory
屬性值。注意:兩者 同時設定時,優先呼叫fallback
,fallbackFactory
不進行呼叫了。
@FeignClient(name="eureka-client",/*fallback=HelloClientFailImpl.class,*/ fallbackFactory = HelloClientFallbackFactory.class)
public interface IHelloClient {
/**
* 定義介面
* @param name
* @return
*/
@RequestMapping(value="/hello", method=RequestMethod.GET)
public String hello(@RequestParam("name") String name);
}
- 建立
fallback
和fallbackFactory
屬性對應類。
HelloClientFailImpl.java
@Component("fallback")
@Slf4j
public class HelloClientFailImpl implements IHelloClient{
@Override
public String hello(String name) {
log.error("restTemplate呼叫[hello]服務發生熔斷,引數name:{}", name);
return "restTemplate呼叫[hello]服務發生熔斷,引數name:" + name;
}
}
HelloClientFallbackFactory/java
@Component
@Slf4j
public class HelloClientFallbackFactory implements FallbackFactory<IHelloClient>{
@Autowired
@Qualifier("fallback")
IHelloClient helloClient;
@Override
public IHelloClient create(Throwable cause) {
log.error("feign呼叫發生異常,觸發熔斷", cause);
return helloClient;
}
}
可以知道,正常fallback
就是一個介面的實現類,當傳送異常時,會呼叫此介面實現類進行服務呼叫。而FallbackFactory
是也是一個介面實現類,需要實現feign.hystrix.FallbackFactory<T>
介面,在發生熔斷時,呼叫create
方法,同時返回被呼叫介面的實現類,以便進行fallback處理。
3.配置檔案開啟feign的熔斷器功能。
feign.hystrix.enabled=true
或者,申明一個Feign.Builder
類也是可以的,我們從org.springframework.cloud.openfeign.FeignClientsConfiguration
可以看出,啟用feign
的條件:
所以正常,我們只需要在配置檔案中加入feign.hystrix.enabled
為true
即可,注意:此屬性在IDE下未進行提示的。
或者就如此類一樣,申明一個bean:
@Bean
public Feign.Builder feignHystrixBuilder() {
return HystrixFeign.builder();
}
也是可以的。
4.編寫一個測試類FeignController
:
/**
* feign 熔斷器示例
* @author oKong
*
*/
@RestController
@Slf4j
public class FeignController {
@Autowired
IHelloClient helloClient;
@GetMapping("/feign")
public String hello(String name) {
log.info("使用feign呼叫服務,引數name:{}", name);
return helloClient.hello(name);
}
}
5.再次啟動應用,訪問:http://127.0.0.1:8038/feign?name=oKong ,正常呼叫如下:
關閉服務提供者,再次訪問,瀏覽器返回了錯誤提示:
同時,我們使用了FallbackFactory
,控制檯打印出了具體異常:
針對熔斷超時時間等相關設定,可以通過@HystrixCommand
註解的各屬性進行配置,主要還是commandProperties
屬性值,具體的引數可檢視com.netflix.hystrix.HystrixCommandProperties
類,也可以針對某個呼叫方法進行特殊設定。具體的可以看看這篇文章:hystrix的基本介紹和配置屬性說明,或者可以去大佬程式設計師DD部落格查閱下關於Hystrix
相關知識點:服務容錯保護(Hystrix斷路器)【Dalston版】、服務容錯保護(Hystrix依賴隔離)【Dalston版】,版本雖然是D版的,但原理是差不多的~
參考資料
總結
本章節主要講解了如何整合
Hystrix
。本身Hystrix
已經包含了服務降級
、依賴隔離
、熔斷器
等功能了,我們使用時並沒進行特殊設定,預設都是生效的。對於一些關於Hystrix
的高階用法,比如訊號量
隔離、執行緒池的設定等等,還有一些像超時時間
等,由於此方面瞭解的不多,這裡就不班門弄斧了。大家可去官方網站或者谷歌搜尋下相關材料下,對於一些業務場景,可進行一些自定義設定。主要還是針對@HystrixCommand
註解的相關配置。關於呼叫統一異常的處理相關實踐,比如當提供方異常時,呼叫方如何進行統一異常處理,或者服務不可用時,進行統一的異常捕獲,告知外圍呼叫者服務不可用等資訊。這些相關實踐部分會在之後的實踐篇系列文章中進行闡述的,也許不是最佳的實踐,僅希望提供一個參考方案吧。這裡就不表了,敬請期待~
最後
目前網際網路上大佬都有分享
SpringCloud
系列教程,內容可能會類似,望多多包涵了。原創不易,碼字不易,還希望大家多多支援。若文中有錯誤之處,還望提出,謝謝。
老生常談
- 個人QQ:
499452441
- 微信公眾號:
lqdevOps