微服務實戰:服務熔斷降級組件 Hystrix 工作原理及用法,實戰講解!
一、背景介紹
在上一篇文章中,我們介紹了 Spring Cloud 技術體系中最核心的組件之一 Feign,它主要作用是簡化 HTTP 客戶端的開發工作,通過簡單的幾個注解配置即可完成 HTTP 調用的綁定操作。
今天通過這篇文章,結合之前的知識,我們一起來了解一下 Spring Cloud 技術體系中另一個最核心的組件之一 Hystrix。
二、Hystrix 簡介
Spring Cloud Hystrix 是一套基于 Netflix Hystrix 實現的斷路器工具,主要作用是處理那些可能會失敗并且需要被隔離的遠程服務調用,以避免導致整個系統崩潰。
在微服務架構中,業務服務之間的調用通常錯綜復雜,比如某個服務接口 A,當收到用戶請求的時候,會調用服務 B 的接口;服務接口 C 可能也會調用服務 B,依次類推。
圖片
這種請求鏈路非常常見,假設服務 B 因為請求量過大而出現無法響應,導致服務不可用。此時所有調用服務 B 的客戶端都會出現異常,甚至會像滾雪球一樣放大到所有相關的服務。
圖片
對于要求高可用的項目來說,顯然這是不可接受的。為了解決這個問題,因此產生了斷路器這類服務保護的工具。
Hystrix 類似于電路中的斷路器,當線路中有點電器發生短路時,此時斷路器就會發揮作用,它會及時的切斷故障電路,防止發生過載、發熱、甚至起火等嚴重后果,以保護線路的安全。
引入 Hystrix 之后,服務的遠程調用邏輯可以用如下圖來簡要概括。
圖片
當調用遠程服務失敗率達到一定閾值時,Hystrix 會自動打開斷路器,阻止對下游服務的進一步調用,從而避免系統資源的浪費和可能的級聯失敗;當下游服務恢復正常之后,斷路器會在一段時間后自動或半自動地關閉,嘗試恢復下游服務調用。
下面我們通過具體的例子,看看如何使用 Hystrix 來實現服務自動熔斷。
三、方案實踐
3.1、Hystrix 使用示例
在此,我們以之前介紹的 Ribbon 為例,依次創建eureka-server、eureka-provider工程,就不重復粘貼了。
3.1.1、創建服務消費者
根據eureka-consumer-ribbon復制一個服務消費者工程,命名為eureka-consumer-ribbon-hystrix,并在pom.xml中引入 Hystrix 依賴包,示例如下:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
3.1.2、開啟掃描 Hystrix 功能
然后,創建一個服務啟動類并添加@EnableCircuitBreaker或者@EnableHystrix注解,表示開啟掃描 Hystrix 功能。
@EnableCircuitBreaker
@EnableDiscoveryClient
@SpringBootApplication
publicclass Application {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
returnnew RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(Application.class,args);
}
}
3.1.3、編寫服務降級方法
接著,創建一個遠程調用服務RpcService并使用@HystrixCommand注解指定服務遠程調用失敗后的降級方法,示例如下:
@Service
publicclass RpcService {
@Autowired
private RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "fallback")
public String hello() {
String url = "http://eureka-provider/hello";
String result = restTemplate.getForObject(url, String.class);
return result;
}
/**
* 服務降級方法
* @return
*/
public String fallback() {
return"發起遠程調用失敗,服務自動熔斷";
}
}
3.1.4、編寫遠程調用類
最后,創建一個controller,通過定義的遠程調用服務發起請求。
@RestController
publicclass HelloController {
@Autowired
private RpcService rpcService;
/**
* 發起遠程調用測試
* @return
*/
@GetMapping("/rpc")
public String rpc() {
String result = rpcService.hello();
return"發起遠程調用,收到返回的信息:" + result;
}
}
3.1.5、服務測試
完成以上工程之后,依次將eureka-server、eureka-provider、eureka-consumer-ribbon-hystrix服務啟動起來。
然后在瀏覽器上訪問eureka-consumer-ribbon-hystrix服務中的/rpc接口,中途將eureka-provider服務停掉,可以得到類似于如下內容。
圖片
可以清晰的看到,當客戶端遠程調用失敗后,斷路器自動開啟并調用指定的服務降級方法。
3.1.6、斷路器閾值設置
默認情況下,被 Hystrix 保護的方法,在 10 秒內,調用次數超過了 20 次并且出現 50% 以上的請求發生失敗, 那么斷路器就會進入打開狀態,所有后續的調用都將會由回調方法處理;如果服務提供者掛了,也會直接進入打開狀態。在 5 秒之后,斷路器進入半開狀態,將會再次嘗試調用原始的方法,如果請求成功,斷路器進入關閉狀態。
如果想要調整斷路器閾值,例如在 20 秒內,調用次數超過 30 次并且失敗率超過 30% 時,斷路器自動進入打開狀態,可以通過如下方式進行配置。
@HystrixCommand(fallbackMethod = "fallback",commandProperties = {
@HystrixProperty(
name = "circuitBreaker.requestVolumeThreshold",
value = "30"),
@HystrixProperty(
name = "circuitBreaker.errorThresholdPercentage",
value = "30"),
@HystrixProperty(
name = "metrics.rollingStats.timeInMilliseconds",
value = "20000"),
@HystrixProperty(
name = "circuitBreaker.sleepWindowInMilliseconds",
value = "60000")
})
public String hello() {
String url = "http://eureka-provider/hello";
String result = restTemplate.getForObject(url, String.class);
return result;
}
3.2、Feign 使用 Hystrix
在介紹 Feign 使用方式中,我們有提到過 Feign 不僅整合了 Ribbon,還整合了 Hystrix。因此,我們可以在 Feign 客戶端中直接使用 Hystrix 相關功能。
根據之前的eureka-consumer-feign復制一個服務消費者工程,命名為eureka-consumer-feign-hystrix,相關的依賴包也一起 copy 過來。
3.2.1、添加相關配置
然后,在application.properties配置文件中,開啟 hystrix 熔斷。
# 在Feign中開啟 hystrix熔斷
feign.hystrix.enabled=true
3.2.2、編寫服務回調類
接著,創建回調類RpcServiceHystrix并實現RpcService接口。
@Component
public class RpcServiceHystrix implements RpcService {
@Override
public String findByName(@RequestParam(value = "name") String name) {
return "請求失敗,服務自動降級,請求參數:" +name;
}
}
3.2.3、指定服務回調類
再然后,在@FeignClient注解中通過fallback屬性指定請求失敗時的回調類;同時,在服務提供者中也增加一個/findByName接口,用于接受請求。
/**
* 配置要調用的服務實例名稱,并指定請求失敗時的回調類
*/
@FeignClient(name = "eureka-provider", fallback = RpcServiceHystrix.class)
public interface RpcService {
/**
* 要調用的目標服務接口地址
* @return
*/
@RequestMapping(value = "/findByName")
String findByName(@RequestParam(value = "name") String name);
}
3.2.4、編寫遠程調用類
最后,創建一個controller,通過定義的 feign 客戶端和 hystrix 熔斷器來調用服務提供方的接口。
@RestController
publicclass HelloController {
@Autowired
private RpcService rpcService;
/**
* 發起遠程調用測試
* @return
*/
@GetMapping("/rpc")
public String rpc(@RequestParam(value = "name") String name) {
String result = rpcService.findByName(name);
return"通過 feign 發起遠程調用(帶 hystrix 熔斷器),收到返回的信息:" + result;
}
}
3.2.5、服務測試
完成以上工程之后,依次將eureka-server、eureka-provider、eureka-consumer-feign-hystrix服務啟動起來。
與之類似,然后在瀏覽器上訪問eureka-consumer-feign-hystrix服務中的/rpc接口,中途將eureka-provider服務停掉,可以得到類似于如下內容。
可以清晰的看到,效果與上文介紹的一樣。當客戶端遠程調用失敗后,斷路器自動開啟并調用指定的服務回調方法。
四、小結
最后總結一下,在微服務架構中,服務之間的依賴關系錯綜復雜,一個服務的失敗可能會影響到調用它的其他服務,進而引發整個系統的崩潰。
Hystrix 就是為了解決這類問題而設計的。Hystrix 具備斷路器模式、服務降級、請求緩存、請求合并、線程隔離、自動回退等強大功能,由 Netflix 開發并開源。
盡管之后 Netflix 宣布對其停止維護,但是 Hystrix 仍然是一個值得學習和了解的重要工具,特別是在理解分布式系統容錯和斷路器模式方面。
五、參考
- http://www.ityouknow.com/springcloud/2017/05/16/springcloud-hystrix.html
- https://www.didispace.com/spring-cloud/springcloud3.html#netflix-hystrix