1. 程式人生 > >SpringCloud Netflix (五) : Hystrix 服務熔斷和服務降級

SpringCloud Netflix (五) : Hystrix 服務熔斷和服務降級

什麼是Hystrix

在分散式環境中,許多服務依賴項中的一些服務依賴不可避免地會失敗。Hystrix是一個庫,通過新增延遲容忍和容錯邏輯,幫助您控制這些分散式服務之間的互動。Hystrix通過隔離服務之間的訪問點、防止服務之間的級聯故障以及提供回退選項來實現這一點,所有這些都提高了系統的總體彈性。

( 級聯故障 )

 

Hystrix可以做什麼

  • 延遲和容錯

    防止級聯故障。回退和優雅的降級。快速恢復失敗。

    帶斷路器的執行緒和訊號量隔離。

  • 實時操作

    實時監控和配置更改。當服務和屬性變更在叢集中傳播時,立即生效。

    保持警覺,做出決定,影響變化,並在幾秒鐘內看到結果。

  • 併發性

    並行執行。支援併發的請求快取。通過請求摺疊自動批處理。

 

服務熔斷和服務降級

服務雪崩

服務熔斷和服務降級是解決服務雪崩的手段之一,所以在瞭解服務熔斷和服務降級前,需要先明白什麼是服務雪崩。如下圖所示,因評論服務的失敗,導致整個服務鏈條的失敗,即一個服務失敗,導致整條鏈路的服務都失敗的情形,我們稱之為服務雪崩。

服務熔斷

如上圖所示,如果當評論服務不可用或響應過慢時,常理來說,應該等到評論服務恢復可用再來呼叫,可事實上是,後續每個評論服務請求,還是會等待評論服務響應,這可能會消耗商品詳情服務的寶貴資源,如執行緒,導致資源耗盡,從而使商品詳情服務無法處理其他請求。而服務熔斷就是解決這個問題的。

當下遊的服務因為某種原因突然變得不可用或響應過慢,上游服務為了保證自己整體服務的可用性,不再繼續呼叫目標服務,直接返回,快速釋放資源。如果目標服務情況好轉則恢復呼叫。需要說明的是熔斷其實是一個框架級的處理,而基本上業內用的是斷路器模式;

注意,這時商品詳情服務還是會因為評論服務請求失敗報Hystrix circuit short-circuited and is OPEN異常而不可用,單純的服務熔斷只是避免重複呼叫不可用的評論服務而已,不要把熔斷和熔斷降級歸為一起,後面實現可以看一些區別。

斷路器背後的基本思想非常簡單。在斷路器物件中包裝受保護的函式呼叫,該物件監視故障。一旦故障達到某一閾值,斷路器就會跳閘,所有對斷路器的進一步呼叫都會返回錯誤,而根本不進行受保護的呼叫。通常,如果斷路器跳閘,您還需要某種監視器警報。 ---Martin Fowler

那斷路器什麼時候開啟和關閉呢?

以Hystrix的斷路器為例,每當20個請求中,有50%失敗時,斷路器就會開啟,此時再呼叫此服務,將會直接返回失敗,不再調遠端服務。直到5s鍾之後,會跳到半開模式,放一次請求進來,重新檢測服務是否恢復正常,判斷是否把斷路器關閉,或者繼續開啟。

服務降級

服務熔斷雖然避免了許多無用的呼叫,但是商品詳情服務還是會因為相比不太重要的評論服務失敗而不可用,那是不合理的。那能不能在評論服務請求失敗時,不影響商品詳情服務的正常使用呢?這時候就需要使用服務降級了。(注意,服務降級有很多種降級方式!如開關降級、限流降級、熔斷降級! 熔斷降級是採用了服務熔斷的降級方式,可以說熔斷降級是服務降級方式的一種,不要把熔斷降級想為單單是熔斷)

降級有兩種場景:

  • 當下遊的服務因為某種原因響應過慢,下游服務主動停掉一些不太重要的業務,釋放出伺服器資源,增加響應速度。(開關降級)

  • 當下遊的服務因為某種原因不可用,上游主動呼叫本地的一些降級邏輯,避免卡頓,迅速返回給使用者。(熔斷降級)

 

Hyrtix實現服務熔斷和服務降級

建立hystrix-details、hystrix-comment、hystrix-goods、hystrix-price服務模擬上圖4個服務,並4個服務註冊到註冊中心。

新增Hystrix依賴

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

實現服務熔斷

在hystrix-details的啟動類上添加註解@EnableCircuitBreaker,允許建立斷路器

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableCircuitBreaker
public class HystrixDetails {
    public static void main(String[] args) {
        SpringApplication.run(HystrixDetails.class, args);
    }
}

在需要熔斷功能的方法上添加註解@HystrixCommand,將方法新增進斷路器監控中

@GetMapping("/details")
@HystrixCommand
public String details() throws RuntimeException, InterruptedException{
    return "goods:"+goodsFeignClient.goods()+
            " price:"+priceFeignClient.price()+
            " comment:"+commentFeignClient.comment();
}

在hystrix-comment被呼叫的方法裡添一行錯誤程式碼或執行緒睡眠,模擬服務不可用或響應過慢,啟動各個服務,訪問 hystrix-details裡details()方法地址localhost:8080/details,快速重新整理20次以上,觸發斷路器開啟。

之後檢視控制檯,會發現當斷路器開啟後,hystrix-comment報錯時間異常間隔為5秒 (hystrix休眠窗時間) ,hystrix-details報錯異常從feign.FeignException: status 500 reading CommentFeignClient#comment(); content: 變成java.lang.RuntimeException: Hystrix circuit short-circuited and is OPEN,說明在斷路器開啟的時間內,hystrix-details對hystrix-comment請求並沒有進入到hystrix-comment服務中,而是被斷路器攔截了,服務熔斷實現成功。一般不會單獨使用熔斷,而會使用熔斷+降級的熔斷降級。

實現服務降級(熔斷降級)

在之前程式碼中的註解@HystrixCommand裡新增 fallbackMethod = "detailsFallback" 並建立detailsFallback方法。

@GetMapping("/details")
@HystrixCommand(fallbackMethod = "detailsFallback")
public String details() throws RuntimeException, InterruptedException{
    return "goods:"+goodsFeignClient.goods()+
            " price:"+priceFeignClient.price()+
            " comment:"+commentFeignClient.comment();
}

public String detailsFallback(){
    return "goods:"+goodsFeignClient.goods()+
            " price:"+priceFeignClient.price()+
            " comment:error";
}

這時候訪問localhost:8080/details,hystrix-detail服務請求hystrix-comment服務失敗後會觸發降級,呼叫退步方法fallback() ,hystrix-detail服務不會報異常,頁面狀態200正常。在這裡的fallback是降級機制的一種,所以只要在@HystrixCommand 註解里加上fallbackMethod屬性值,就不單單是熔斷了,而是熔斷降級。

 

FallbackFactory

當我們實際使用服務降級時,不應該使用上面這種方式。當一個訪問中要呼叫多個服務時,fallback的回退方法就會非常臃腫,後期維護困難,程式碼耦合度高,且一個方法一個fallback也增加了程式碼量。所以我們應該面向服務,把每個服務的fallback包裝起來,在呼叫服務的介面上實現fallback,FallbackFactory就是hystrix提供給我們來實現這一舉措的。

1.建立一個CommentFallbackFactory類實現FallbackFactory<T>介面

@Component
public class CommentFallbackFactory implements FallbackFactory<CommentFeignClient> {
    @Override
    public CommentFeignClient create(Throwable throwable) {
        return new CommentFeignClient() {
            @Override
            public String comment() {
                return "error";
            }
        };
    }
}

2.在呼叫hystrix-comment服務的feign介面的@FeignClient里加上

fallbackFactory = CommentFallbackFactory.class

@Component //必須填加,否則應用會掃描不到
@FeignClient(value = "HYSTRIX-COMMENT", fallbackFactory = CommentFallbackFactory.class)
public interface CommentFeignClient {
    @GetMapping("/")
    String comment();
}

3.在配置裡開啟feign的hystrix

feign允許開啟hystrix後, 會自動把所有服務的feign介面下的方法加入到斷路器監控中

feign:
  hystrix:
    enabled: true

 

HystrixCommandProperties

我們可以在配置裡修改 HystrixCommandProperties 類(在com.netflix.hystrix包下)裡的變數,包括修改斷路器的休眠窗時間circuitBreakerSleepWindowInMilliseconds、修改響應超時時間executionTimeoutInMilliseconds

hystrix:
  command:
    default:  #default全域性有效,service id指定應用有效
    #配置的屬性名在HystrixCommandProperties類的構造方法下可以找到
      execution:
        timeout:
          enabled: true #是否開啟超時熔斷
      circuitBreaker:
        sleepWindowInMilliseconds: 10000 #把斷路器的休眠窗時間設為10秒,預設為5秒

 

Hystrix儀表盤

Hystrix的主要好處之一是它收集的有關每個HystrixCommand的一組度量。Hystrix儀表板以有效的方式顯示每個斷路器的執行狀況。

1.新建module,springcloud-consumer-hystrix-dashboard

新增 hystrix-dashboard 相關依賴

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

新增配置

@SpringBootApplication
//開啟儀表盤
@EnableHystrixDashboard
public class HystrixDashboard {

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

}

2.在服務提供者

新增監控依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

在啟動類新增ServletRegistrationBean

@SpringBootApplication
@MapperScan("com.example.springcloud.mapper")
@EnableDiscoveryClient
public class Provider_8002 {

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

    @Bean
    public ServletRegistrationBean servletRegistrationBean(){
        ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
        registrationBean.addUrlMappings("/actuator/hystrix.stream");
        return registrationBean;
    }
}

3.測試訪問

訪問 localhost:9001/hystrix

輸入要監控的微服務 http://localhost:8002/actuator/hystrix.stream

&n