1. 程式人生 > >Hystrix流程分析及斷路器工作原理

Hystrix流程分析及斷路器工作原理

上一篇轉載的文章主要講了Hystrix的應用場景、基礎元件概念以及從原始碼的角度闡述了基本應用。本篇文章主要借鑑官方文件介紹Hystrix的工作流程及斷路器的原理,最後說一下與SpringCloud的簡單整合。

How it Works

先上一個官方的流程圖:

在這裡插入圖片描述
這個圖大概描述了Hystrix的工作流程:
按照圖中綠色標識的步驟:
1、建立一個HystrixCommand或HystrixObservableCommand物件;
2、通過以下四種方法執行命令(前兩種方法僅適用於簡單HystrixCommand物件且不可用HystrixObservableCommand):

  • execute():阻塞的,返回從依賴項收到的單個響應(或在出現錯誤時丟擲異常)
  • queue(): 非阻塞的,返回一個可以從依賴項中獲取單個響應的方法的Future物件。
  • observe(): 訂閱Observable表示來自依賴項的響應,並返回Observable複製該源的響應Observable
  • toObservable(): 返回一個Observable,如果訂閱了,會執行Hystrix命令併發出其響應

3、如果為此請求設定了快取,首先判斷快取是否是可用的(即是否可以從快取得到本次請求的響應),如果可以得到則直接返回,得不到再走下一步。
4、判斷斷路器是否開啟,如果打開了說明之前的請求失敗了(或者說之前滿足了斷路器的開啟條件,具體滿足的開閉條件下一模組再說),則直接呼叫重寫的fallback()函式;如果斷路器未開啟再到下一步。
5、判斷與該command關聯的執行緒池和佇列(或訊號量)是否已滿,如果已經滿了,則Hystrix不會執行該Command,直接呼叫fallback()返回;否則到下一步。
6、此時通過HystrixObservableCommand.construct()或HystrixCommand.run()執行目的方法呼叫對依賴項的請求,如果執行失敗或超時則直接執行fallback()。
7、健康狀況統計
Hystrix在執行過程中會記錄斷路器的成功,失敗,拒絕和超時狀態,Hystrix維護一個記錄這些資料的計數器,計數器的資料決定斷路器的開閉狀態。
8、當命令失敗時Hystrix都會嘗試回退,一般情況下,如果實現了HystrixCommand.getFallback()會返回單個回退值,或者實現HystrixObservableCommand.resumeWithFallback()會發出一個或多個回退值的Observable。如果沒有實現fallback方法或在執行fallback方法時丟擲了異常,Hystrix仍然會返回一個Observable,但不會返回任何內容,並立即終止併發出onError通知。通過此onError通知,導致命令失敗的異常被傳回給呼叫者。
9、成功返回

斷路器

還是先上一個官方給出的邏輯決策圖,包括計數器如何決定斷路器的開閉。
在這裡插入圖片描述

說明:
1、如果請求數達到了設定的請求閾值或者請求失敗的比例超過了設定的比例,則斷路器將從close狀態轉到open狀態,這時所有的請求都會被阻止。
2、sleep一段時間後,下一個請求將被放過,這時斷路器處於半開半閉狀態,目的是為了驗證一下後邊的路是否通暢,如果請求失敗,則斷路器回到open狀態;如果成功了則斷路器切換到closed狀態並且返回響應的結果。

圖中下方描述了計數器維護的資料儲存結構及工作原理:大概意思是它維護10個桶(bucket),每個桶中記錄第i秒請求狀態(success、failure、timeout、rejection)的數量,當新的一秒請求記錄來的時候,計數器會丟掉時間最靠前的桶。

SpringCloud簡單整合

這個比較簡單,註冊中心選用Eureka(Consul的可以自己去測),直接上程式碼

model-1:eureka-server

Application.java

@EnableEurekaServer
@SpringBootApplication
public class Application {

	public static void main(String[] args) {
		new SpringApplicationBuilder(Application.class).web(true).run(args);
	}
}

application.properties

spring.application.name=eureka-server
server.port=1001

eureka.instance.hostname=localhost
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false

logging.file=${spring.application.name}.log
model-2:eureka-consumer-ribbon-hystrix

Application.java

@EnableCircuitBreaker
@EnableDiscoveryClient
@SpringBootApplication
public class Application {

	@Bean
	@LoadBalanced
	public RestTemplate restTemplate() {
		return new RestTemplate();
	}

	public static void main(String[] args) {
		new SpringApplicationBuilder(Application.class).web(true).run(args);
	}
}

DcController.java

@RestController
public class DcController {

    @Autowired
    ConsumerService consumerService;

    @GetMapping("/consumer")
    public String dc() {
        return consumerService.consumer();
    }

    @Service
    class ConsumerService {

        @Autowired
        RestTemplate restTemplate;

        @HystrixCommand(fallbackMethod = "fallback")
        public String consumer() {
            return restTemplate.getForObject("http://eureka-client/dc", String.class);
        }

        public String fallback() {
            return "fallbck";
        }

    }

}

application.properties

spring.application.name=eureka-consumer-ribbon-hystrix
server.port=2101

eureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/

logging.file=${spring.application.name}.log
model-3:eureka-client

Application.java

@EnableDiscoveryClient
@SpringBootApplication
public class Application {

	public static void main(String[] args) {
		new SpringApplicationBuilder(Application.class).web(true).run(args);
	}
}

DcController.java

@RestController
public class DcController {

    @Autowired
    DiscoveryClient discoveryClient;

    @GetMapping("/dc")
    public String dc() {
        String services = "Services: " + discoveryClient.getServices();
        System.out.println(services);
        return services;
    }

}

application.properties

spring.application.name=eureka-client
server.port=2001

eureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/
#eureka.client.serviceUrl.defaultZone=http://peer1:1001/eureka/,http://peer2:1002/eureka/

logging.file=${spring.application.name}.log

Hystrix官方文件:Netflix-Hystrix官方WIKI
更多SpringCloud基礎教程訪問:程式設計師DD老司機的SpringCloud教程

本文已在版權印備案,如需轉載請訪問版權印06630244