1. 程式人生 > 實用技巧 >Hystrix斷路器

Hystrix斷路器

Hystrix斷路器

解決問題: 主要防止伺服器叢集發生雪崩, 起到對伺服器的保護作用

github地址: https://github.com/Netflix/Hystrix/wiki

介紹

背景

分散式系統環境下,服務間類似依賴非常常見,一個業務呼叫通常依賴多個基礎服務。如下圖,對於同步呼叫,當庫存服務不可用時,商品服務請求執行緒被阻塞,當有大批量請求呼叫庫存服務時,最終可能導致整個商品服務資源耗盡,無法繼續對外提供服務。並且這種不可用可能沿請求呼叫鏈向上傳遞,這種現象被稱為雪崩效應。

圖解

雪崩效應常見場景

  • 硬體故障:如伺服器宕機,機房斷電,光纖被挖斷等。
  • 流量激增:如異常流量,重試加大流量等。
  • 快取穿透:一般發生在應用重啟,所有快取失效時,以及短時間內大量快取失效時。大量的快取不命中,使請求直擊後端服務,造成服務提供者超負荷執行,引起服務不可用。
  • 程式BUG:如程式邏輯導致記憶體洩漏,JVM長時間FullGC等。
  • 同步等待:服務間採用同步呼叫模式,同步等待造成的資源耗盡。

雪崩效應應對策略

針對造成雪崩效應的不同場景,可以使用不同的應對策略,沒有一種通用所有場景的策略,參考如下:

  • 硬體故障:多機房容災、異地多活等。
  • 流量激增:服務自動擴容、流量控制(限流、關閉重試)等。
  • 快取穿透:快取預載入、快取非同步載入等。
  • 程式BUG:修改程式bug、及時釋放資源等。
  • 同步等待:資源隔離、MQ解耦、不可用服務呼叫快速失敗等。資源隔離通常指不同服務呼叫採用不同的執行緒池;不可用服務呼叫快速失敗一般通過熔斷器模式結合超時機制實現。

綜上所述,如果一個應用不能對來自依賴的故障進行隔離,那該應用本身就處在被拖垮的風險中。 因此,為了構建穩定、可靠的分散式系統,我們的服務應當具有自我保護能力,當依賴服務不可用時,當前服務啟動自我保護功能,從而避免發生雪崩效應。本文將重點介紹使用Hystrix解決同步等待的雪崩問題。

Hystrix兩大功能

  • 降級,超時、出錯、不可到達時,對服務降級,返回錯誤資訊或者是快取資料
  • 熔斷,當服務壓力過大,錯誤比例過多時,熔斷所有請求,所有請求直接降級

降級

一個服務呼叫後臺服務失敗(出現異常、等待超時、不能連線),可以執行當前服務中的一段程式碼,向前返回響應(錯誤提示、快取資料)

系統容錯,當後臺服務出現錯誤,還可以向客戶端返回結果

熔斷

10秒內20次請求,50%呼叫失敗,執行了降級程式碼,會觸發熔斷

熔斷可以避免故障的傳播,避免引起雪崩效應

限流,後臺服務壓力過大出現故障,可以斷開連線,限制訪問流量

熔斷的條件(兩個條件必須都滿足)

  1. 10秒20次請求(首先滿足)
  2. 50%失敗,執行了降級程式碼

半開狀態

​ 斷路器開啟5秒後,會進入半開狀態,

​ 客戶端請求時,會嘗試傳送一次呼叫,

​ 如果成功,會自動關閉斷路器,恢復正常

​ 如果失敗,就繼續保持開啟狀態

Hystrix的使用

和ribbon聯用 [跳轉ribbon]

1. maven依賴

在springboot中匯入Hystrix [Maintenance] 依賴

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

2. 新增主程式註解

主要是把@EnableCircuitBreaker註解到主啟動類

以下3個註解可以使用@SpringCloudApplication註解代替

@EnableCircuitBreaker // 開啟Hystrix的註解, 啟動斷路器
//@EnableDiscoveryClient // 開啟eureka的註解, 高版本可省略
@SpringBootApplication
public class Sp06RibbonApplication {

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

    // ribbon配置, RestTemplate物件
	@LoadBalanced
	@Bean
	public RestTemplate restTemplate() {
		SimpleClientHttpRequestFactory f = new SimpleClientHttpRequestFactory();
		f.setConnectTimeout(1000);
		f.setReadTimeout(1000);
		return new RestTemplate();
	}

}

3. 降級斷路示例

使用@HystrixCommand註解來實現降級, 實現步驟如下:

  1. 使用 @HystrixCommand(fallbakMethod="降級方法名")註解, 放在正常執行的業務程式碼方法上, 這個業務員程式碼中呼叫了後端的伺服器, 引數fallbakMethod為字串型別, 指向降級的方法名
  2. 然後再同一個類下, 宣告降級的方法, 即上面fallbakMethod引數指定的方法

如下程式碼

@RestController
@Slf4j
public class RibbonController {

    // 注入RestTemplate物件 (ribbon)
    @Autowired
    private RestTemplate restTemplate;

    // 處理請求業務, 呼叫後端伺服器
    @GetMapping("/item-service/{orderId}")
    @HystrixCommand(fallbackMethod = "getItemsFB") // 使用降級註解
    public JsonResult<List<Item>> getItems(@PathVariable String orderId) {
        log.info("呼叫後臺商品服務, 獲取商品列表");
        JsonResult r = restTemplate.getForObject(
                "http://item-service/{1}",
                JsonResult.class,
                orderId);
        return r;
    }
    
    // 降級後執行的方法
    public JsonResult<List<Item>> getItemsFB(@PathVariable String orderId) {
        return JsonResult.err().msg("獲取商品失敗");
    }
    
}

即, 當我們的正常業務getItems方法呼叫後端伺服器(出現異常、等待超時[超時時間預設為1秒]、不能連線)時, 執行getItemsFB方法

Hystrix 超時設定

hystrix等待超時後, 會執行降級程式碼, 快速向客戶端返回降級結果, 預設超時時間是1000毫秒

思考: 當呼叫後端伺服器超時的時候(Hystrix預設超時時間為1秒), 伺服器是否會進行重試或者更換伺服器的操作(ribbon)??????

假如如下為ribbon的超時時間(1秒)

@LoadBalanced
@Bean
public RestTemplate restTemplate() {
    SimpleClientHttpRequestFactory f = new SimpleClientHttpRequestFactory();
    f.setConnectTimeout(1000);
    f.setReadTimeout(1000);
    return new RestTemplate();
}

以及重試和更換伺服器的次數

ribbon:
  MaxAutoRetriesNextServer: 2
  MaxAutoRetries: 1
  OkToRetryOnAllOperations: true

答案也是很明顯的重試依然會執行, 也會執行降級方法的執行結果, 但是我們的重試和更換伺服器的操作幾乎沒有意義了

那麼我們如何解決這一問題呢?

我們可以設定Hystrix 超時時間, 即降級執行的時間, 此設定一般應大於 ribbon 的重試超時時長,例如 10 秒

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 10000

如果呼叫後端服務超時, hystrix 才會立即執行降級方法

如果呼叫的後端服務未啟動, hystrix也會立即執行降級方法

dashboard 斷路器儀表盤

hystrix 對請求的降級和熔斷,可以產生監控資訊,hystrix dashboard可以實時的進行監控

儀表盤一般都是獨立執行的

斷路儀表盤依賴springboot的actuator工具 關於actuator功能 [actuator監控]

1. actuator依賴

在被監控的專案中(Hystrix專案)新增actuator依賴, 並配置了配置檔案

依賴:

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

yml配置檔案

management:
  endpoints:
    web:
      exposure:
        include: "*"  # 一定要加引號

被監控專案啟動後訪問: http://localhost:3001/actuator (ip和埠寫自己的) 檢視效果

如果一直顯示ping:, 可傳送一次請求, 出現一大坨資訊就是配置成功了

2. 建立儀表盤專案

我們重新新建一個專案, 用來監控

匯入Hystrix Dashboard依賴 :

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

3. 配置檔案

配置檔案只需要進行簡單的配置即可

spring:
  application:
    name: hystrix-dashboard   # 服務名
    
server:
  port: 4001   # 埠號

4. 添加註解

在主啟動類上新增一個註解@EnableHystrixDashboard

@EnableHystrixDashboard
@SpringBootApplication
public class Sp08HystrixDashboardApplication {

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

}

5. 訪問hystrix dashboard

http://localhost:4001/hystrix ip/域名和埠寫自己的即可

可以看到如下頁面:

然後輸入我們的被監控的Hystrix服務的網址, 點選Monitor Stream即可

我的是http://localhost:3001/actuator, 我就填入這個

關於儀表盤的資訊如何檢視:

hystrix 熔斷

整個鏈路達到一定的閾值,預設情況下,10秒內產生超過20次請求,則符合第一個條件。
滿足第一個條件的情況下,如果請求的錯誤百分比大於閾值,則會開啟斷路器,預設為50%
Hystrix的邏輯,先判斷是否滿足第一個條件,再判斷第二個條件,如果兩個條件都滿足,則會開啟斷路器

斷路器開啟 5 秒後,會處於半開狀態,會嘗試轉發請求,如果仍然失敗,保持開啟狀態,如果成功,則關閉斷路器

併發訪問測試

使用 apache 的併發訪問測試工具 ab

http://httpd.apache.org/docs/current/platform/windows.html#down

  • 用 ab 工具,以併發50次,來發送20000個請求

    ab -n 20000 -c 50 http://localhost:3001/item-service/35
    
  • 斷路器狀態為 Open,所有請求會被短路,直接降級執行 fallback 方法

hystrix 配置

https://github.com/Netflix/Hystrix/wiki/Configuration

  • hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds
    請求超時時間,超時後觸發失敗降級
  • hystrix.command.default.circuitBreaker.requestVolumeThreshold
    10秒內請求數量,預設20,如果沒有達到該數量,即使請求全部失敗,也不會觸發斷路器開啟
  • hystrix.command.default.circuitBreaker.errorThresholdPercentage
    失敗請求百分比,達到該比例則觸發斷路器開啟
  • hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds
    斷路器開啟多長時間後,再次允許嘗試訪問(半開),仍失敗則繼續保持開啟狀態,如成功訪問則關閉斷路器,預設 5000

https://github.com/benwang6/spring-cloud-repo