1. 程式人生 > 實用技巧 >【SpringCloud】Hystrix熔斷器

【SpringCloud】Hystrix熔斷器

Hystrix熔斷器

概述

分散式系統面臨的問題

分散式系統面臨的問題
複雜分散式體系結構中的應用程式有數10個依賴關係,每個依賴關係在某些時候將不可避免地失敗

服務雪崩
多個微服務之間呼叫的時候,假設微服務A呼叫微服務B和微服務C,微服務B和微服務C又呼叫其它的微服務,這就是所謂的“扇出” 。
如果扇出的鏈路.上某個微服務的呼叫響應時間過長或者不可用,對微服務A的呼叫就會佔用越來越多的系統資源,進而引起系統崩潰,所謂的” 雪崩效應”。

對於高流量的應用來說,單一的後端依賴可能會導致所有伺服器上的所有資源都在幾秒鐘內飽和。比失敗更糟糕的是,這些應用程式還可能導致服務之間的延遲增加,備份佇列,執行緒和其他系統資源緊張,導致整個系統發生更多的級聯故障。這些都表示需要對故障和延遲進行隔離和管理,以便單個依賴關係的失敗,不能取消整個應用程式或系統。

所以,通常當你發現一個模組下的某個例項失敗後,這時候這個模組依然還會接收流量,然後這個有問題的模組還呼叫了其他的模組,這樣就會發生級聯故障,或者叫雪崩。

是什麼

Hystrix是一個用於處理分散式系統的延遲和容錯的開源庫 ,在分散式系統裡,許多依賴不可避免的會呼叫失敗,比如超時、異常等,Hystrix能夠保證在一個依賴出問題的情況下, 不會導致整體服務失敗,避免級聯故障,以提高分散式系統的彈性。

“斷路器”本身是一種開關裝置, 當某個服務單元發生故障之後,通過斷路器的故障監控(類似熔斷保險絲),向呼叫方返回一個符合預期的、可處理的備選響應(FallBack) ,而不是長時間的等待或者丟擲呼叫方無法處理的異常,這樣就保證了服務呼叫方的執行緒不會被長時間、不必要地佔用,從而避免了故障在分散式系統中的蔓延,乃至雪崩。

能幹嘛

  • 服務降級
  • 服務熔斷
  • 接近實時的監控

官網資料

https://github.com/Netflix/hystrix/wiki

Hystrix官宣,停更進維

https://github.com/Netflix/hystrix

後果:

  • 被動修復bugs
  • 不再接受合併請求
  • 不再發布新版本

HyStrix重要概念

服務降級

伺服器忙,請稍後再試,不讓客戶端等待並立刻返回一個友好提示,fallback

哪些情況會發出降級

  • 程式執行異常
  • 超時
  • 服務熔斷觸發服務降級
  • 執行緒池/訊號量也會導致服務降級

服務熔斷

類比保險絲達到最大服務訪問後,直接拒絕訪問,拉閘限電,然後呼叫服務降級的方法並返回友好提示
就是保險絲:服務熔斷->進而服務降級->恢復呼叫鏈路

服務限流

秒殺高併發等操作,嚴禁一窩蜂的過來擁擠,大家排隊,一秒鐘N個,有序進行

hystrix案例

構建

新建cloud-provider-hystrix-payment8001

POM

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud2020</artifactId>
        <groupId>com.atguigu.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-provider-hystrix-payment8001</artifactId>

    <dependencies>
        <!--hystrix-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
        <!--eureka client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-common</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--監控-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--熱部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

YML

server:
  port: 8001
spring:
  application:
    name: cloud-provider-hystrix-payment
eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka

主啟動

package com.atguigu.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

/**
 * @author zzyy
 * @create 2020/3/6 22:21
 **/
@SpringBootApplication
@EnableEurekaClient
public class PaymentHystrixMain8001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentHystrixMain8001.class, args);
    }
}

業務類

service

package com.atguigu.springcloud.service;

import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * @author zzyy
 * @create 2020/3/6 22:23
 **/
@Service
public class PaymentService {
    /**
     * 正常訪問
     *
     * @param id
     * @return
     */
    public String paymentInfo_OK(Integer id) {
        return "執行緒池:" + Thread.currentThread().getName() + " paymentInfo_OK,id:" + id + "\t" + "O(∩_∩)O哈哈~";
    }

    /**
     * 超時訪問
     *
     * @param id
     * @return
     */
    public String paymentInfo_TimeOut(Integer id) {
        int timeNumber = 3;
        try {
            // 暫停3秒鐘
            TimeUnit.SECONDS.sleep(timeNumber);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "執行緒池:" + Thread.currentThread().getName() + " paymentInfo_TimeOut,id:" + id + "\t" +
                "O(∩_∩)O哈哈~  耗時(秒)" + timeNumber;
    }
}

controller

package com.atguigu.springcloud.controller;

import com.atguigu.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * @author zzyy
 * @create 2020/3/6 22:30
 **/
@RestController
@Slf4j
public class PaymentController {
    @Resource
    private PaymentService paymentService;

    @Value("${server.port}")
    private String servicePort;

    /**
     * 正常訪問
     *
     * @param id
     * @return
     */
    @GetMapping("/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id) {
        String result = paymentService.paymentInfo_OK(id);
        log.info("*****result:" + result);
        return result;
    }

    /**
     * 超時訪問
     *
     * @param id
     * @return
     */
    @GetMapping("/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
        String result = paymentService.paymentInfo_TimeOut(id);
        log.info("*****result:" + result);
        return result;

    }
}

正常測試

啟動eureka7001

啟動eureka-provider-hystrix-payment8001

訪問

success的方法

http://localhost:8001/payment/hystrix/ok/31

每次呼叫耗費5秒鐘

http://localhost:8001/payment/hystrix/timeout/31

上述module均OK

以上述為根基平臺,從正確->錯誤->降級熔斷->恢復

高併發測試

上述在非高併發情形下,還能勉強滿足 but...

Jmeter壓測測試

下載地址
https://jmeter.apache.org/download_jmeter.cgi

開啟Jmeter,來20000個併發壓死8001,20000個請求都去訪問paymentInfo_TimeOut服務

再來一個訪問

http://localhost:8001/payment/hystrix/timeout/31

看演示結果

兩個都在轉圈圈

為什麼會被卡死?
tomcat的預設工作執行緒數被打滿了,沒有多餘的執行緒來分解壓力和處理

Jmeter壓測結論

上面還只是服務提供者8001自己測試,假如此時外部的消費者80也來訪問,那消費者只能乾等,最終導致消費端80不滿意,服務端8001直接被拖死

看熱鬧不嫌棄事大,80新建加入

cloud-consumer-feign-hystrix-order80

新建

cloud-consumer-feign-hystrix-order80

POM
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud2020</artifactId>
        <groupId>com.atguigu.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-consumer-feign-hystrix-order80</artifactId>

    <dependencies>
        <!--openfeign-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <!--eureka client-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
        <dependency>
            <groupId>com.atguigu.springcloud</groupId>
            <artifactId>cloud-api-common</artifactId>
            <version>${project.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--監控-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--熱部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>
YML
server:
  port: 80
eureka:
  client:
    register-with-eureka: false
    fetch-registry: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
主啟動
package com.atguigu.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 * @author zzyy
 * @date 2020/02/18 17:20
 **/
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class OrderHystrixMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderHystrixMain80.class, args);
    }
}
業務類
PaymentHystrixService
package com.atguigu.springcloud.service;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

/**
 * @author zzyy
 * @create 2020/3/6 23:19
 **/
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT")
public interface PaymentHystrixService {


    /**
     * 正常訪問
     *
     * @param id
     * @return
     */
    @GetMapping("/payment/hystrix/ok/{id}")
    String paymentInfo_OK(@PathVariable("id") Integer id);

    /**
     * 超時訪問
     *
     * @param id
     * @return
     */
    @GetMapping("/payment/hystrix/timeout/{id}")
    String paymentInfo_TimeOut(@PathVariable("id") Integer id);

}
OrderHyrixController
@RestController
@Slf4j
public class OrderHystrixController {

    @Resource
    private PaymentHystrixService paymentHystrixService;

    @GetMapping("/consumer/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id){
        String result = paymentHystrixService.paymentInfo_OK(id);
        return  result;
    }

    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    public String paymentInfo_Timeout(@PathVariable("id") Integer id){
        String result = paymentHystrixService.paymentInfo_Timeout(id);
        return  result;
    }
}
正常測試

http://localhost/consumer/payment/hystrix/ok/32

高併發測試

開啟Jmeter,來20000個併發壓死8001

消費者80微服務再去訪問的OK服務8001地址

http://localhost/consumer/payment/hystrix/ok/32

消費者80,o(╥﹏╥)o

  • 要麼轉圈圈
  • 要麼消費端報超時錯誤

故障和導致現象

  • 8001同一層次的其他介面被困死,因為tomcat執行緒池裡面的工作執行緒已經被擠佔完畢
  • 80此時呼叫8001,客戶端訪問響應緩慢,轉圈圈

上述結論

正因為有上述故障或不佳表現#才有我們的降級/容錯/限流等技術誕生

如何解決?解決的要求

超時導致伺服器變慢(轉圈)

超時不再等待

出錯(宕機或程式執行出錯)

出錯要有兜底

解決

  • 對方服務(8001)超時了,呼叫者(80)不能一直卡死等待,必須有服務降級
  • 對方服務(8001)down機了,呼叫者(80)不能一直卡死等待,必須有服務降級
  • 對方服務(8001)ok,呼叫者(80)自己有故障或有自我要求(自己的等待時間小於服務提供者)

服務降級

降級配置

@HystrixCommand

8001先從自身找問題

設定自身呼叫超時時間的峰值,峰值內可以正常執行,#超過了需要有兜底的方法處理,做服務降級fallback

8001fallback

業務類啟用

@HystrixCommand報異常後如何處理
一旦呼叫服務方法失敗並丟擲了錯誤資訊後,會自動呼叫@HystrixCommand標註好的fallbckMethod呼叫類中的指定方法
@HystrixCommand(fallbackMethod = "paymentInfo_TimeoutHandler",commandProperties = {
        @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds",value = "3000")
})
public String paymentInfo_Timeout(Integer id) {
    int timeNumber = 5;
    try {
        TimeUnit.SECONDS.sleep(timeNumber);
    } catch (Exception e){
         e.printStackTrace();
    }
    return "執行緒池: " + Thread.currentThread().getName()
            + "   paymentInfo_OK,id:" + id + " 耗時(秒):" + timeNumber;
}

public String paymentInfo_TimeoutHandler(Integer id) {
    return "/(ToT)/呼叫支付介面超時或異常、\t" + "\t當前執行緒池名字" + Thread.currentThread().getName();
}
圖示

主啟動類啟用

@EnableCircuitBreaker

80fallback

80訂單微服務,也可以更好的保護自己,自己也依樣畫葫蘆進行客戶端端降級保護

題外話

我們自己配置過的熱部署方式對java程式碼的改動明顯,但對@HystrixCommand內屬性的修改建議重啟微服務

POM

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

YML

server:
  port: 80
eureka:
  client:
    register-with-eureka: false
    fetch-registry: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
feign:
  hystrix:
    enabled: true

主啟動

@EnableHystrix

業務類

@GetMapping("/consumer/payment/hystrix/timeout/{id}")
@HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod", commandProperties = {
        @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")
})
public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
    //int age = 10/0;
    return paymentHystrixService.paymentInfo_TimeOut(id);
}

public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id) {
    return "我是消費者80,對方支付系統繁忙請10秒種後再試或者自己執行出錯請檢查自己,o(╥﹏╥)o";
}

目前問題

  • 每個業務方法對應一個兜底的方法,程式碼膨脹
  • 統一和自定義的分開

解決辦法

每個方法配置一個???膨脹

feign介面系列
@DefaultProperties(defaultFallback="")


說明

@DefaultProperties(defaultFallback = ")
1: 1每個方法配置一 個服務降級方法,技術上可以,實際上傻X
1: N除了個別重要核心業務有專屬,它普通的可以通過@DefaultProperties(defaultFallback= ")統- -跳轉到統- 處理結果頁面

通用的和獨享的各自分開,避免了程式碼膨脹,合理減少了程式碼量, 0(∩∩)O哈哈~

controller配置
package com.atguigu.springcloud.controller;

import com.atguigu.springcloud.service.PaymentHystrixService;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * @author zzyy
 * @create 2020/3/6 23:20
 **/
@RestController
@Slf4j
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")
public class OrderHystrixController {
    @Resource
    private PaymentHystrixService paymentHystrixService;


       @GetMapping("/consumer/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id) {
        return paymentHystrixService.paymentInfo_OK(id);
    }

       @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    /*@HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod", commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")
    })*/
    @HystrixCommand
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
        //int age = 10/0;
        return paymentHystrixService.paymentInfo_TimeOut(id);
    }

    public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id) {
        return "我是消費者80,對方支付系統繁忙請10秒種後再試或者自己執行出錯請檢查自己,o(╥﹏╥)o";
    }

    /**
     * 全域性fallback
     *
     * @return
     */
    public String payment_Global_FallbackMethod() {
        return "Global異常處理資訊,請稍後重試.o(╥﹏╥)o";
    }
}

和業務邏輯混在一起???混亂

服務降級,客戶端去呼叫服務端,碰上服務端宕機或關閉
本次案例服務降級處理是在客戶端80實現完成,與服務端8001沒有關係#只需要為Feign客戶端定義的介面新增一個服務降級處理的實現類即可實現解耦
未來我們要面對的異常
  • 執行
  • 超時
  • 宕機
再看我們的業務類PaymentController

修改cloud-consumer-feign-hystrix-order80
根據cloud-consumer-feign-hystrix-order80已經有的PaymentHystrixService介面,重新新建一個類(PaymentFallbackService)實現介面,統一為接口裡面的方法進行異常處理
PaymentFallbackService類實現PaymentFeginService介面

YML

記得開啟這個註解

feign:
  hystrix:
    enabled: true

PaymentFeignClientService介面

測試

單個eureka先啟動7001

PaymentHystrixMain8001啟動

正常訪問測試:http://localhost/consumer/payment/hystrix/ok/32

故意關閉微服務8001

客戶端自己呼叫提示
此時服務端provider已經down了 ,但是我們做了服務降級處理,#讓客戶端在服務端不可用時也會獲得提示資訊而不會掛起耗死伺服器

服務熔斷

斷路器

一句話就是家裡的保險絲

熔斷是什麼

熔斷機制概述
熔斷機制是應對雪崩效應的一種微服務鏈路保護機制。 當扇出鏈路的某個微服務出錯不可用或者響應時間太長時,會進行服務的降級,進而熔斷該節點微服務的呼叫,快速返回錯誤的響應資訊。
當檢測到該節點微服務呼叫響應正常後,恢復呼叫鏈路。

在Spring Cloud框架裡,熔斷機制通過Hystrix實現。Hystrix會監控微服務間呼叫的狀況,
當失敗的呼叫到一定閾值,預設是5秒內20次呼叫失敗,就會啟動熔斷機制。熔斷機制的註解是@HystrixCommand.

大神論文:https://martinfowler.com/bliki/CircuitBreaker.html

實操

修改cloud-provider-hystrix-payment8001

PaymentService

// 服務熔斷
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
        @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),              //是否開啟斷路器
        @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),    //請求數達到後才計算
        @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"), //休眠時間窗
        @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"),  //錯誤率達到多少跳閘
})
public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
      if(id < 0){
          throw  new RuntimeException("****id 不能為負數");
      }
      String serialNumber = IdUtil.simpleUUID();

      return  Thread.currentThread().getName() + "\t" + "呼叫成功,流水號:" + serialNumber;
}

public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id){
     return "id 不能為負數,請稍後再試, o(╥﹏╥)o id: " + id;
}
why這些引數

PaymentController

@GetMapping("/payment/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
    String result = paymentService.paymentCircuitBreaker(id);
    log.info("*****result: " + result);
    return result;
}

測試

自測cloud-provider-hystrix-payment8001

正確:http://localhost:8001/payment/circuit/31
錯誤:http://localhost:8001/payment/circuit/-31

一次正確一次錯誤trytry

重點測試
多次正確,然後慢慢正確,發現剛開始不滿足條件,就算是正確的訪問也不能進行

原理/小總結

大神結論


熔斷型別

熔斷開啟

請求不再呼叫當前服務,內部設定一般為MTTR(平均故障處理時間),當開啟長達導所設時鐘則進入半熔斷狀態

熔斷關閉

熔斷關閉後不會對服務進行熔斷

熔斷半開

部分請求根據規則呼叫當前服務,如果請求成功且符合規則則認為當前服務恢復正常,關閉熔斷

官網斷路器流程圖

官網步驟

斷路器在什麼情況下開始起作用


涉及到斷路器的三個重要引數:快照時間窗、請求總數閥值、錯誤百分比閥值。

  1. 快照時間窗:斷路器確定是否開啟需要統計一些請求和錯誤資料, 而統計的時間範圍就是快照時間窗,預設為最近的10秒。
  2. 請求總數閥值:在快照時間窗內,必須滿足請求總數閥值才有資格熔斷。預設為20,意味著在10秒內,如果該hystrix命令的呼叫次數不足20次,即使所有的請求都超時或其他原因失敗,斷路器都不會開啟。
  3. 錯誤百分比閥值:當請求總數在快照時間窗內超過了閥值,比如發生了30次呼叫,如果在這30次呼叫中,有15次發生了超時異常,也就是超過50%的錯誤百分比,在預設設定50%閥值情況下,這時候就會將斷路器開啟。
斷路器開啟或者關閉的條件
  1. 當滿足一定的閾值的時候(預設10秒鐘超過20個請求次數)
  2. 當失敗率達到一定的時候(預設10秒內超過50%的請求次數)
  3. 到達以上閾值,斷路器將會開啟
  4. 當開啟的時候,所有請求都不會進行轉發
  5. 一段時間之後(預設5秒),這個時候斷路器是半開狀態,會讓其他一個請求進行轉發. 如果成功,斷路器會關閉,若失敗,繼續開啟.重複4和5
斷路器開啟之後
  1. 再有請求呼叫的時候,將不會呼叫主邏輯,而是直接呼叫降級fallback。通過斷路器,實現了自動地發現錯誤並將降級邏輯切換為主邏輯,減少響應延遲的效果。

  2. 原來的主邏輯要如何恢復呢?
    對於這一-問題,hystrix也為我們實現了 自動恢復功能。
    當斷路器開啟,對主邏輯進行熔斷之後,hystrix會啟動- 個休眠時間窗, 在這個時間窗內, 降級邏輯是臨時的成為主邏輯,當休眠時間窗到期,斷路器將進入半開狀態,釋放- -次請求到原來的主邏輯上,如果此次請求正常返回,那麼斷路器將繼續閉合,主邏輯恢復,如果這次請求依然有問題,斷路器繼續進入開啟狀態,休眠時間窗重新計時。

ALI配置




服務限流

後面高階篇講解alibaba的Sentinel說明

hystrix工作流程

https://github.com/Netflix/Hystrix/wiki/How-it-Works

Hystrix工作流程

官網圖例

步驟說明

服務監控hystrixDashboard

概述

除了隔離依賴服務的呼叫以外,Hystrix還提供 了準實時的呼叫監控(Hystrix Dashboard),Hystrix會持續地記錄所有通過Hystrix發起的請求的執行資訊,以統計報表和圖形的形式展示給使用者,包括每秒執行多少請求多少成功,多少失敗等。Netflix通過hystrix- metrics-event-stream專案實現了對以上指標的監控。Spring Cloud也提供了Hystrix Dashboard的整合,對監控內容轉化成視覺化介面。

儀表盤9001

新建cloud-consumer-hystrix-dashboard9001

POM

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>cloud2020</artifactId>
        <groupId>com.atguigu.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-consumer-hystrix-dashboard9001</artifactId>
    <description>hystrix監控</description>


    <dependencies>
        <!--hystrix dashboard-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
        </dependency>
        <!--監控-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--熱部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

YML

server:
  port: 9001

HystrixDashboardMain9001+新註解@EnableHystrixDashboard

package com.atguigu.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;

/**
 * @author zzyy
 * @create 2020/3/7 17:27
 **/
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardMain9001 {
    public static void main(String[] args) {
        SpringApplication.run(HystrixDashboardMain9001.class);
    }
}

所有Provider微服務提供類(8001/8002/8003)都需要監控依賴部署

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

啟動cloud-consumer-hystrix-dashboard9001該微服務後續將監控微服務8001

http://localhost:9001/hystrix

斷路器演示(服務監控hystrixDashboard)

修改cloud-provider-hystrix-payment8001

注意:新版本Hystrix需要在主啟動MainAppHystrix8001中指定監控路徑

package com.atguigu.springcloud;

import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.context.annotation.Bean;

/**
 * @author zzyy
 * @create 2020/3/7 17:27
 **/
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardMain9001 {
    public static void main(String[] args) {
        SpringApplication.run(HystrixDashboardMain9001.class);
    }

    /**
     * 此配置是為了服務監控而配置,與服務容錯本身無觀,springCloud 升級之後的坑
     * ServletRegistrationBean因為springboot的預設路徑不是/hystrix.stream
     * 只要在自己的專案中配置上下面的servlet即可
     * @return
     */
    @Bean
    public ServletRegistrationBean getServlet(){
        HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
        ServletRegistrationBean<HystrixMetricsStreamServlet> registrationBean = new ServletRegistrationBean<>(streamServlet);
        registrationBean.setLoadOnStartup(1);
        registrationBean.addUrlMappings("/hystrix.stream");
        registrationBean.setName("HystrixMetricsStreamServlet");
        return registrationBean;
    }
}

Unable to connect to Command Metric Stream.

404

監控測試

啟動一個eureka或者3個eureka叢集均可

觀察監控視窗

9001監控8001

填寫監控地址
http://localhost:8001/hystrix.stream

測試地址

http://localhost:8001/payment/circuit/31
http://localhost:8001/payment/circuit/-31
上述測試通過
ok

先訪問正確地址,再訪問錯誤地址,再正確地址,會發現圖示斷路器都是慢慢放開的.

  • 監控結果,成功

  • 監控結果,失敗

如何看?
7色

1圈

實心圓:共有兩種含義。它通過顏色的變化代表了例項的健康程度,它的健康度從綠色<黃色<橙色<紅色遞減。
該實心圓除了顏色的變化之外,它的大小也會根據例項的請求流量發生變化,流量越大該實心圓就越大。所以通過該實心圓的展示,就可以在大量的例項中快速的發現故障例項和高壓力例項。

1線

曲線:用來記錄2分鐘內流量的相對變化,可以通過它來觀察到流量的上升和下降趨勢。

整圖說明


整圖說明2

搞懂一個才能看懂複雜的