天天看點

SpringCloud(Hystrix)

Hystrix 熔斷器

    • 1. Hystrix 介紹
      • 1.1 是什麼:
      • 1.2 Hystrix設計原則:
      • 1.3 Hystrix特性
    • 2. 圖解
    • 3. SpringCloud 內建Hystrix
      • 3.1 maven依賴
      • 3.2 添加注解
      • 3.Hystrix的服務熔斷
    • 4. Hystrix的服務降級
    • 5. Hystrix的異常處理
      • 5.1 未調用遠端服務前發生異常
      • 5.2 調用遠端服務發生異常
      • 5.3 忽略異常
      • 5.4 自定義Hystrix的服務異常熔斷
        • 5.4.1 同步調用
        • 5.4.1 異步調用
    • 6. Hystrix的儀表盤監控
      • 6.1 添加maven依賴
      • 6.2 啟動類添加注解
      • 6.3 配置檔案中配置服務端口号
      • 6.4 配置要監控的項目
        • 6.4.1 首先需要引入Hystrix以及Dashboard的依賴
        • 6.4.1 修改需要監控的項目配置

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的庫,實作了熔斷器模式。在微服務架構中,它通常有多個服務調用層。

微服務圖
SpringCloud(Hystrix)
Hystrix回退以防止連鎖故障
SpringCloud(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]。

這個問題看官方文檔的話,應該可以避免,見下圖:)

SpringCloud(Hystrix)
<!--正确版本,應該是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 路徑),頁面如下圖所示:

SpringCloud(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

SpringCloud(Hystrix)

配置後,點選監控即可。

SpringCloud(Hystrix)

繼續閱讀