天天看點

Hystrix斷路器

Hystrix斷路器

Hystrix斷路器
服務雪崩
  • 多個微服務之間調用的時候,假設微服務A調用微服務B和微服務C,微服務B和微服務C又調用其他微服務,這就是所謂的 “扇出”,如果扇出的鍊路上某個微服務的調用時間過長或者不可用,對微服務A的調用就會占有越來越多的系統資源,進而引起系統奔潰,所謂的 “雪崩效應”。
  • 對于高流量的應用來說,單一的後端依賴坑會導緻所有伺服器上的所有資源都在幾秒内飽和。更糟糕的是,這些應用程式還可能導緻服務之間的延遲增加,備份隊列,線程和其他系統資源緊張,導緻整個系統發送更多的級聯故障。這些表示需要對故障和延遲進行隔離和管理,以便單個依賴關系失敗,不能導緻整個應用程式奔潰
  • 是以 通常當你發現一個子產品下的某個執行個體失敗後,這時候這個子產品依然還會接收-流量,然後這個有問題的子產品還調用了其他子產品,這樣就會發生級聯故障,或者叫雪崩。
簡介
  • Hystrix 是一個用于處理分布式系統的延遲和容錯的開源庫,在分布式系統裡,許多依賴不可避免的會調用失敗,比如逾時,異常等,Hystrix能保證一個依賴出問題的情況下,不會導緻整個服務失敗,避免級聯故障,以提高分布式系統的彈性。
  • “斷路器 ” 本身是一種開關裝置,當某個服務單元發生故障的時候,通過斷路器的故障監控(類似熔斷保險絲),==向調用方傳回一個符合預期,可處理的備選響應(FallBack),而不是長時間的等待護着抛出調用方無法處理的異常==,這樣就保證了服務調用方的線程不會被長時間、不必要地占用,進而避免故障在分布式系統中蔓延
  • 功能:服務降級,服務熔斷,接近實時的監控,限流,隔離等
Hystrix重要概念

服務降級(fallback)

提供者和消費者都可以進行服務降級。(一般都是放在用戶端(消費者))

Hystrix斷路器

服務熔斷(break)

Hystrix斷路器

服務限流(flowlimit)

Hystrix斷路器

建立用戶端 cloud-provider-hystrix-payment8001 子產品

  1. 引入POM (在用戶端(消費者使用))
<!-- hystrix-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>           
  1. yml配置
server:
  port: 8001


spring:
  application:
    name: cloud-provider-hystrix-payment
    

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      #單機版
      defaultZone: http://localhost:7001/eureka
           
  1. 主啟動類
@EnableEurekaClient
@SpringBootApplication
public class PaymentHystrixMain8001 {

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

}
           
  1. service
@Service
public class PaymentService {

    //正常通路方法
    public String paymentInfo_OK(Integer id){
        return "線程池:" + Thread.currentThread().getName() + "\tpaymentInfo_OK,id:" + id;
    }


    //逾時通路方法
    public String paymentInfo_TimeOut(Integer id){
        int timeNumber = 3;
        try {
            TimeUnit.SECONDS.sleep(timeNumber);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "線程池:" + Thread.currentThread().getName() +
                "\tpaymentInfo_TimeOut,id:" + id + ",耗時:" + timeNumber + "秒";
    }

}           
  1. controller
@Slf4j
@RestController
public class PaymentController {

    @Resource
    PaymentService paymentService;

    @Value("${server.port}")    //spring的@Value注解
    private String ServerPort;

    @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;
    }

    @GetMapping("/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(Integer id){
        String result = paymentService.paymentInfo_OK(id);
        log.info("******result:" + result);
        return result;
    }

}           
  1. 啟動7001和8001

==

http://localhost:8001/payment/hystrix/ok/1==
Hystrix斷路器
http://localhost:8001/payment/hystrix/timeout/1==
Hystrix斷路器

進行高并發測試:

Hystrix斷路器
Hystrix斷路器

然後去通路

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

,通路速度變慢了。

建立消費者

建立消費者子產品 cloud-consumer-feign-hystrix-order80

  1. 引入POM
<dependencies>
    <!-- openfeign -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
    <!--   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-client</artifactId>
    </dependency>
    <!-- 引用自己定義的api通用包,可以使用Payment支付Entity -->
    <dependency>
        <groupId>com.angenin.springcloud</groupId>
        <artifactId>cloud-api-commons</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>
           
  1. yml檔案
server:
  port: 80


eureka:
  client:
    register-with-eureka: false
    service-url:
      defaultZone: http://localhost:7001/eureka

#需要加上,否則會報錯
ribbon:
  ReadTimeout: 4000
  ConnectTimeout: 4000
           
@EnableEurekaClient
@EnableFeignClients
@SpringBootApplication
public class OrderHystrixMain80 {

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

}
           
@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT")
public interface PaymentHystrixService {

    @GetMapping("/payment/hystrix/ok/{id}")
    public String paymentInfo_OK(@PathVariable("id") Integer id);
    
    @GetMapping("/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}           
@Slf4j
@RestController
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;
    }

}
           
  1. 啟動jmeter,然後再進行測試
Hystrix斷路器

安裝JMeter

JMeter下載下傳位址:

http://jmeter.apache.org/download_jmeter.cgi

下載下傳tgz和zip都可以:

Hystrix斷路器

啟動jmeter 測試

Hystrix斷路器

故障現象、導緻原因以及解決

現象:

Hystrix斷路器

解決:

Hystrix斷路器
服務降級
Hystrix斷路器

提供者 (8001子產品)

  1. 修改8001中PaymentService的paymentInfo_TimeOut方法,并添加paymentInfo_TimeOutHandler方法:
@HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler", commandProperties = {
            //設定自身逾時調用時間的峰值為3秒,峰值内可以正常運作,超過了需要有兜底的方法處理,服務降級fallback
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
    })
    public String paymentInfo_TimeOut(Integer id){
        int timeNumber = 5;
        //int i = 1 / 0;
        try {
            TimeUnit.SECONDS.sleep(timeNumber);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "線程池:" + Thread.currentThread().getName() +
                "\tpaymentInfo_TimeOut,id:" + id + ",耗時:" + timeNumber + "秒";
    }
    public String paymentInfo_TimeOutHandler(Integer id){
        return "8001提供者,線程池:" + Thread.currentThread().getName() + 
                "\tpaymentInfo_TimeOutHandler系統繁忙,請稍後再試,id:" + id;
    }           
  1. 然後在8001的主啟動類上添加@EnableCircuitBreaker注解,啟用斷路器。
Hystrix斷路器

消費者 (80子產品)

Hystrix斷路器
  1. yml添加 :
feign:
  hystrix:
    enabled: true
           
  1. 在主啟動類添加@EnableHystrix注解。
  2. 修改OrderHystrixController的paymentInfo_TimeOut方法,并添加paymentTimeOutFallbackMethod方法:
@HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod", commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")
    })
    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id){
        String result = paymentHystrixService.paymentInfo_TimeOut(id);
        return result;
    }
    public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id){
        return "消費者80,支付系統繁忙";
    }
           

啟動7001,8001,80,

http://localhost/consumer/payment/hystrix/timeout/1

(如果是提供者那邊出問題,并且消費者設定了fallback,會優先進入消費者的fallback)

Hystrix斷路器
Hystrix斷路器

代碼膨脹的解決辦法:

Hystrix斷路器
Hystrix斷路器

服務熔斷

Hystrix斷路器
  1. 在8001的PaymentService中添加
在這裡插入代碼片    @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
        @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),                      //開啟斷路器
        @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),         //請求總數門檻值(預設20)
        @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"),   //休眠時間視窗期(休眠多久進入半開模式(機關毫秒,預設5秒))
        @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"),       //請求次數的錯誤率達到多少跳閘(百分率%,預設50%)
})
    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 不能為負數,請稍後再試, id: " + id;
    }
           
  1. 在8001的PaymentController中添加
@GetMapping("/payment/circuit/{id}")
public String paymentCircuitBreaker(@PathVariable("id") Integer id){
    String result = paymentService.paymentCircuitBreaker(id);
    log.info("******result:" + result);
    return result;
}           
  1. http://localhost:8001/payment/circuit/-1 (輸入超過6次進入熔斷)
Hystrix斷路器
  • 熔斷10秒内就算是正确的請求也傳回錯誤資訊。
  • 10秒後進入半開模式,對請求進行處理,此時如果是正确的請求,那麼就關閉熔斷,否則再次進入熔斷,10秒後再次開啟半開模式,對請求進行處理,直到半開模式處理到正确請求。

總結

https://martinfowler.com/bliki/CircuitBreaker.html
Hystrix斷路器
  • 我的總結:如果請求次數的錯誤率超過指定值,開啟熔斷,經過一段時間後,變為半開模式,然後放進一個請求進行處理,如果請求處理成功,關閉熔斷;如果還是報錯,繼續進入熔斷,再經過一段時間後,變為半開模式,再進行對下一個請求進行處理,一直在熔斷,半開模式來回切換,直到請求成功,關閉熔斷。
Hystrix斷路器
  • 官網步驟:
Hystrix斷路器
  • 斷路器在什麼情況下開始起作用:
    Hystrix斷路器
Hystrix斷路器
  • 斷路器開啟或關閉的條件:
    Hystrix斷路器

斷路器打開之後:

Hystrix斷路器
服務限流

會在後面進階篇alibaba的Sentinel講解。

Hystrix工作流程:

Hystrix斷路器
Hystrix斷路器

服務監控HystrixDashboard

Hystrix斷路器

儀表盤9001

  1. 建立子產品 cloud-consumer-hystrix-dashboard9001
  2. pom
<dependencies>
    <!--   hystrix儀表盤圖形化     -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
    </dependency>
    <dependency>
        <groupId>com.angenin.springcloud</groupId>
        <artifactId>cloud-api-commons</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>           
  1. http://localhost:9001/hystrix
Hystrix斷路器

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

注意:所有微服務提供者都需要在pom中引入監控依賴。

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

在8001的主啟動類中添加:

/**
    * 此配置是為了服務監控而配置,與服務容錯本身無關,springcloud更新後的坑
    * ServletRegistrationBean因為SpringBoot的預設路徑不是 “/hystrix.stream"
    * 隻要在自己的項目裡配置上下的servlet就可以了
    */
   @Bean
   public ServletRegistrationBean getServlet() {
       HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet() ;
       ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
       registrationBean.setLoadOnStartup(1);
       registrationBean.addUrlMappings("/hystrix.stream");
       registrationBean.setName("HystrixMetricsStreamServlet");
       return  registrationBean;
   }           
Hystrix斷路器

在浏覽器輸入

http://localhost:8001/payment/circuit/1

Hystrix斷路器
Hystrix斷路器
Hystrix斷路器