SpringCloud(Hystrix)

1. Hystrix 介紹

1.1 是什麼:

在分佈式環境中,許多服務依賴項中的一些必然會失敗。Hystrix是一個庫,通過添加延遲容忍和容錯邏輯,幫助你控制這些分佈式服務之間的交互。Hystrix通過隔離服務之間的訪問點、停止級聯失敗和提供回退選項來實現這一點,所有這些都可以提高系統的整體彈性。簡單點說就是當被調用方沒有響應,向調用方直接返回一個錯誤響應即可,而不是長時間的等待,這樣避免調用時因爲等待而線程一直得不到釋放。

1.2 Hystrix設計原則:

  • 1.防止單個服務的故障,耗盡整個系統服務的容器(比如tomcat)的線程資源,避免分佈式環境裏大量級聯失敗。通過第三方客戶端訪問(通常是通過網絡)依賴服務出現失敗、拒絕、超時或短路時執行回退邏輯
  • 2.用快速失敗代替排隊(每個依賴服務維護一個小的線程池或信號量,當線程池滿或信號量滿,會立即拒絕服務而不會排隊等待)和優雅的服務降級;當依賴服務失效後又恢復正常,快速恢復
  • 3.提供接近實時的監控和警報,從而能夠快速發現故障和修復。監控信息包括請求成功,失敗(客戶端拋出的異常),超時和線程拒絕。如果訪問依賴服務的錯誤百分比超過閾值,斷路器會跳閘,此時服務會在一段時間內停止對特定服務的所有請求
  • 4.將所有請求外部系統(或請求依賴服務)封裝到HystrixCommand或HystrixObservableCommand對象中,然後這些請求在一個獨立的線程中執行。使用隔離技術來限制任何一個依賴的失敗對系統的影響。每個依賴服務維護一個小的線程池(或信號量),當線程池滿或信號量滿,會立即拒絕服務而不會排隊等待

1.3 Hystrix特性

  • 1.請求熔斷: 當Hystrix Command請求後端服務失敗數量超過一定比例(默認50%), 斷路器會切換到開路狀態(Open). 這時所有請求會直接失敗而不會發送到後端服務. 斷路器保持在開路狀態一段時間後(默認5秒), 自動切換到半開路狀態(HALF-OPEN). 這時會判斷下一次請求的返回情況, 如果請求成功, 斷路器切回閉路狀態(CLOSED), 否則重新切換到開路狀態(OPEN). Hystrix的斷路器就像我們家庭電路中的保險絲, 一旦後端服務不可用, 斷路器會直接切斷請求鏈, 避免發送大量無效請求影響系統吞吐量, 並且斷路器有自我檢測並恢復的能力.
  • 2.服務降級:Fallback相當於是降級操作. 對於查詢操作, 我們可以實現一個fallback方法, 當請求後端服務出現異常的時候, 可以使用fallback方法返回的值. fallback方法的返回值一般是設置的默認值或者來自緩存.告知後面的請求服務不可用了,不要再來了。
  • 3.依賴隔離(採用艙壁模式,Docker就是艙壁模式的一種):在Hystrix中, 主要通過線程池來實現資源隔離. 通常在使用的時候我們會根據調用的遠程服務劃分出多個線程池.比如說,一個服務調用兩外兩個服務,你如果調用兩個服務都用一個線程池,那麼如果一個服務卡在哪裏,資源沒被釋放後面的請求又來了,導致後面的請求都卡在哪裏等待,導致你依賴的A服務把你卡在哪裏,耗盡了資源,也導致了你另外一個B服務也不可用了。這時如果依賴隔離,某一個服務調用A B兩個服務,如果這時我有100個線程可用,我給A服務分配50個,給B服務分配50個,這樣就算A服務掛了,我的B服務依然可以用。
  • 4.請求緩存:比如一個請求過來請求我userId=1的數據,你後面的請求也過來請求同樣的數據,這時我不會繼續走原來的那條請求鏈路了,而是把第一次請求緩存過了,把第一次的請求結果返回給後面的請求。
  • 5.請求合併:我依賴於某一個服務,我要調用N次,比如說查數據庫的時候,我發了N條請求發了N條SQL然後拿到一堆結果,這時候我們可以把多個請求合併成一個請求,發送一個查詢多條數據的SQL的請求,這樣我們只需查詢一次數據庫,提升了效率。

2. 圖解

以下數據、圖來自官方文檔:https://cloud.spring.io/spring-cloud-static/spring-cloud-netflix/1.3.5.RELEASE/single/spring-cloud-netflix.html#_circuit_breaker_hystrix_clients

Netfilix創建了一個名爲Hystrix的庫,實現了熔斷器模式。在微服務架構中,它通常有多個服務調用層。

微服務圖
微服務圖

Hystrix回退以防止連鎖故障
Hystrix回退以防止連鎖故障
一個底層服務的故障會引發直至用戶交互層的連鎖故障。在一個設定時長爲“metrics.rollingStats.timeInMilliseconds”(默認爲十秒)的滾動窗口內,對一個特定服務的請求數大於“circuitBreaker.requestVolumeThreshold”(默認爲20個),並且故障率大於“circuitBreaker.errorThresholdPercentage”(默認大於百分之五十)的時候,啓用熔斷機制以使請求失效。在熔斷和報錯的情況下,開發者可以啓用回退機制。
啓用熔斷機制能防止連鎖故障的情況,給故障服務提供時間以恢復正常。回退操作可以是另一個Hystrix受保護的調用、靜態數據或是一個恰當的空值。回退操作可能是成串的,所以第一個回退操作會做一些其他的業務請求,讓故障回退到預設的值。

3. SpringCloud 集成Hystrix

3.1 maven依賴

注意:添加maven依賴時,注意查看自己的SpringBoot 項目是什麼版本的,因爲 ServerPropertiesAutoConfiguration 在SpringBoot 2.x.x 移除了。如果你的SpringBoot 版本是2.x.x版本就不要引入這個依賴

<!--舊版本使用的-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-hystrix</artifactId>
    <version>1.4.7.RELEASE</version>
</dependency>

注意:應該引入以下這個版本。(別問我爲什麼知道?因爲我就引錯了。。。啓動報錯:org.springframework.beans.factory.BeanDefinitionStoreException: Failed to process import candidates for configuration class [com.kinglong.springcloud.ComsumerApplication]; nested exception is java.io.FileNotFoundException: class path resource [org/springframework/boot/autoconfigure/web/ServerPropertiesAutoConfiguration.class] cannot be opened because it does not exist
at org.springframework.context.annotation.ConfigurationClassParser.processImports(ConfigurationClassParser.java:596) ~[spring-context-5.1.8.RELEASE.jar:5.1.8.RELEASE]。
這個問題看官方文檔的話,應該可以避免,見下圖:)
在這裏插入圖片描述

<!--正確版本,應該是netflix的-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    <version>2.1.2.RELEASE</version>
</dependency>

3.2 添加註解

Boot app 樣例:

啓動類

/**
 * @SpringBootApplication
 * @EnableEurekaClient//eureka客戶端支持
 * @EnableCircuitBreaker//開啓熔斷器
 * 這三個註解在SpringCloud裏可以合併爲一個註解:@SpringCloudApplication,效果等同於這三個註解
 */
@SpringCloudApplication
public class ComsumerApplication {

	public static void main(String[] args) {
		SpringApplication.run(ComsumerApplication.class, args);
	}

}

解釋:爲什麼可以這樣呢?進入這個註解@SpringCloudApplication內部看可以看到:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootApplication
@EnableDiscoveryClient //此註解作用和@EnableEurekaClient一樣,但是還將可以主持其他服務註冊器
@EnableCircuitBreaker
public @interface SpringCloudApplication {
}

3.Hystrix的服務熔斷

/**
 * web api
 *
 * @author haojinlong
 */
@RestController
@RequestMapping("/api/webs")
public class WebController {

	@Autowired
	private RestTemplate restTemplate;
	
   /**
	 * @HystrixCommand由Netflix contrib library提供,被稱作“javanica”
	 * Spring Cloud會自動將包含該註釋的Spring bean封裝在連接到Hystrix熔斷器的代理中
	 * 熔斷器會計算何時啓用或關閉熔斷機制,並決定在故障時該做什麼。
	 * 在需要考慮熔斷的方法上加上該註解即可,fallbackMethod 爲回退時的指定操作方法;
	 * commandProperties:指定超時熔斷時間;
	 * 
	 * @return
	 */
	@RequestMapping("/hello")
	@HystrixCommand(fallbackMethod = "error",commandProperties = {@HystrixProperty (name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")})//1.5秒超時
	public String hello() {
		return restTemplate.getForEntity("http://01-SPRINGCLOUD-SERVICE-PROVIDER/api/hellos/hello", String.class).getBody();
	}

   /**
     * 熔斷的回調方法,也就是降級方法
	 * @return
	 */
	public String error(){
	//訪問遠程服務失敗,處理邏輯寫在這裏即可
		return "error";
	}
}

4. Hystrix的服務降級

前文提到過:Fallback相當於是降級操作. 對於查詢操作, 我們可以實現一個fallback方法, 當請求後端服務出現異常的時候, 可以使用fallback方法返回的值. fallback方法的返回值一般是設置的默認值或者來自緩存.告知後面的請求服務不可用了,不要再來了。這種做法,雖然不能得到正確的返回結果,但至少保證了服務的可用性,比直接拋出錯誤或服務不可用要好很多,可以根據業務場景自行選擇。

5. Hystrix的異常處理

我們在調用服務提供者時,我們自己也有可能會拋出異常,默認情況下方法拋出了異常會自動進行服務降級,交給服務降級中的方法去處理。當我們自己發生異常後,只需要在服務降級方法中添加一個Throwable類型的參數就能夠獲取拋出的異常的類型。

5.1 未調用遠程服務前發生異常

我們在上文的代碼中模擬一個異常

/**
	 * @HystrixCommand由Netflix contrib library提供,被稱作“javanica”
	 * Spring Cloud會自動將包含該註釋的Spring bean封裝在連接到Hystrix熔斷器的代理中
	 * 熔斷器會計算何時啓用或關閉熔斷機制,並決定在故障時該做什麼。
	 *
	 * @return
	 */
	@RequestMapping("/hello")
	@HystrixCommand(fallbackMethod = "error", commandProperties = {@HystrixProperty (name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")})//1.5秒超時
	public String hello() {
		int i= 10/0;
		return restTemplate.getForEntity("http://01-SPRINGCLOUD-SERVICE-PROVIDER/api/hellos/hello", String.class).getBody();
	}

   /**
     * 熔斷的回調方法,也就是降級方法
	 * @return
	 */
	public String error(){
	//訪問遠程服務失敗,處理邏輯寫在這裏即可
		return "error";
	}

測試顯示,未調用前發生異常也會觸發熔斷,調用服務是不會觸發的,直接拋出運行時異常,並且可以在降級方法裏獲取到這個異常。

5.2 調用遠程服務發生異常

將某個遠程服務加入異常代碼。測試顯示,效果如上,可以在降級方法裏獲取到這個異常。

5.3 忽略異常

ignoreExceptions = RuntimeException.class 忽略異常,這樣可以避免調用降級方法,根據業務場景選擇

/**
 * web api
 *
 * @author haojinlong
 */
@RestController
@RequestMapping("/api/webs")
public class WebController {

	@Autowired
	private RestTemplate restTemplate;
	
   /**
	 * @HystrixCommand由Netflix contrib library提供,被稱作“javanica”
	 * Spring Cloud會自動將包含該註釋的Spring bean封裝在連接到Hystrix熔斷器的代理中
	 * 熔斷器會計算何時啓用或關閉熔斷機制,並決定在故障時該做什麼。
	 * 在需要考慮熔斷的方法上加上該註解即可,fallbackMethod 爲回退時的指定操作方法;
	 * commandProperties:指定超時熔斷時間;
	 * ignoreExceptions = RuntimeException.class 忽略異常,這樣可以避免調用降級方法,根據業務場景選擇
	 * 
	 * @return
	 */
	@RequestMapping("/hello")
	@HystrixCommand(fallbackMethod = "error",ignoreExceptions = RuntimeException.class, commandProperties = {@HystrixProperty (name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")})//1.5秒超時
	public String hello() {
		return restTemplate.getForEntity("http://01-SPRINGCLOUD-SERVICE-PROVIDER/api/hellos/hello", String.class).getBody();
	}

   /**
     * 熔斷的回調方法,也就是降級方法
	 * @return
	 */
	public String error(){
	//訪問遠程服務失敗,處理邏輯寫在這裏即可
		return "error";
	}
}

5.4 自定義Hystrix的服務異常熔斷

自定義類繼承HystrixCommand類來實現自定義的Hystrix的請求,在getFallback方法中調用getExecutionException()方法獲取服務拋出異常。

5.4.1 同步調用

/**
 * web api
 *
 * @author haojinlong
 */
@RestController
@RequestMapping("/api/webs")
public class WebController {

	@Autowired
	private RestTemplate restTemplate;
	/**
	 *  com.netflix.hystrix.HystrixCommand用全路徑引用類是防止和@HystrixCommand這個註解混淆
	 *  MyHystrixCommand自定義熔斷器
	 *  execute()調用是同步調用,會一直等待返回結果
	 *
	 * @return
	 */
	@RequestMapping("/hystrix")
	public String hystrix() {
		return new MyHystrixCommand(com.netflix.hystrix.HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("")), restTemplate, "http://01-SPRINGCLOUD-SERVICE-PROVIDER/api/hellos/hello").execute();
	}

}

5.4.1 異步調用

/**
 * web api
 *
 * @author haojinlong
 */
@RestController
@RequestMapping("/api/webs")
public class WebController {

	@Autowired
	private RestTemplate restTemplate;
	/**
	 *  com.netflix.hystrix.HystrixCommand用全路徑引用類是防止和@HystrixCommand這個註解混淆
	 *  MyHystrixCommand自定義熔斷器
	 *  execute()調用是同步調用,會一直等待返回結果
	 *
	 * @return
	 */
	@RequestMapping("/hystrix")
	public String hystrix() {
		Future<String> queue = new MyHystrixCommand(com.netflix.hystrix.HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("")), restTemplate, "http://01-SPRINGCLOUD-SERVICE-PROVIDER/api/hellos/hello").queue();
		String data = queue.get(20000, TimeUnit.MILLISECONDS);
		return data;
	}

}

自定義熔斷器

package com.kinglong.springcloud.hystrix;

import com.netflix.hystrix.HystrixCommand;
import org.springframework.web.client.RestTemplate;

/**
 * 自定義的Hystrix
 *
 * @author haojinlong
 */
public class MyHystrixCommand extends HystrixCommand<String> {

	private RestTemplate restTemplate;

	private String url;

	public MyHystrixCommand (Setter setter, RestTemplate restTemplate){
		super(setter);
		this.restTemplate = restTemplate;
	}

	public MyHystrixCommand (Setter setter, RestTemplate restTemplate, String url){
		super(setter);
		this.restTemplate = restTemplate;
		this.url = url;
	}

	@Override
	protected String run() throws Exception {
		//調用遠程的服務
		return restTemplate.getForEntity(url, String.class).getBody();
	}

	/**
	 * 當遠程服務超時、異常、不可用時,調用該方法
	 *
	 * @return
	 */
	@Override
	protected String getFallback() {
		//實現服務熔斷、降級處理
		return "error";
	}
}

6. Hystrix的儀表盤監控

在微服務架構中爲例保證程序的可用性,防止程序出錯導致網絡阻塞,出現了斷路器模型。斷路器的狀況反應了一個程序的可用性和健壯性,它是一個重要指標。Hystrix Dashboard是作爲斷路器狀態的一個組件,提供了數據監控和友好的圖形化界面。通過監控該面板,可以很直觀的看到每一個服務請求在短時間內(10s)的請求量,以及成功率,失敗率,耗時等信息,可以給後期的系統優化提供依據。

這個服務搭建十分快速,使用SpringBoot搭建三步走即可(我是新建的一個項目)

6.1 添加maven依賴

maven依賴最新版本即可

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
    <version>2.1.2.RELEASE</version>
</dependency>

6.2 啓動類添加註解

@SpringBootApplication
@EnableHystrixDashboard//打開儀表盤功能
public class DashBoardApplication {

	public static void main(String[] args) {
		SpringApplication.run(DashBoardApplication.class, args);
	}

}

6.3 配置文件中配置服務端口號

server.port=3721

大功告成,然後起服即可(訪問路徑爲:http://localhost:3721/hystrix ,記得加上 /hystrix 路徑),頁面如下圖所示:
在這裏插入圖片描述

6.4 配置要監控的項目

6.4.1 首先需要引入Hystrix以及Dashboard的依賴

<dependency>
    <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
     <version>2.1.2.RELEASE</version>
 </dependency>
 <!--springboot提供的一個健康檢查監控的依賴-->
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-actuator</artifactId>
 </dependency>

6.4.1 修改需要監控的項目配置

#用來暴露endpoints的,由於endpoints包含很多敏感信息,除了health、info兩個支持直接訪問外,其他的默認不支持直接訪問,可以指定某個端點訪問,如下
#management.endpoints.web.exposure.include=hystrix.stream
#現在做測試,所以用通配符,可以訪問所有端點
management.endpoints.web.exposure.include=*

至此,就配置好了
剩下的就是在先前的web頁面裏添加這個路徑即可:http://localhost:8081/actuator/hystrix.stream
在這裏插入圖片描述
配置後,點擊監控即可。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章