天天看點

【SpringCloud】Hystrix斷路器【五】

    • 1. Hystrix了解
      • 1.1 概述
      • 1.2 背景
      • 1.3 雪崩效應常見場景
      • 1.4 雪崩效應應對政策
      • 1.5 初探Hystrix
      • 1.6 Hystrix流程圖
    • 2. 建構問題項目
      • 2.2 建構cloud-provider-hystrix-payment8007項目
        • 2.2.1 pom檔案
        • 2.2.2 yml檔案
        • 2.2.3 啟動類
        • 2.2.4 控制器
        • 2.2.5 普通測試
        • 2.2.6 高并發測試
      • 2.3 建構cloud-consumer-hystrix-order6007項目
        • 2.3.1 pom檔案
        • 2.3.2 yml檔案
        • 2.3.3 啟動類
        • 2.3.4 Feign的Service接口
        • 2.3.5 控制器
        • 2.3.6 普通測試
        • 3.2.7 對8007高并發測試
    • 3 服務降級
      • 3.1 概述
      • 3.2 服務提供者8007服務降級(逾時)
        • 3.2.1 啟動類
        • 3.2.2 控制器類
        • 3.2.3 測試
      • 3.3 服務消費者6007服務降級(異常)
        • 3.3.1 啟動類
        • 3.3.2 控制器
        • 3.3.3 測試
      • 3.4 簡化代碼結構
        • 3.4.1 預設Fallback方法
        • 3.4.2 OpenFeign與Hystrix合用
    • 4. 服務熔斷
      • 4.1 概述
      • 4.1.1 熔斷器是什麼
        • 4.1.2 熔斷器狀态
        • 4.1.3 執行過程
      • 4.2 案例示範
        • 4.2.1 HystrixController
        • 4.2.2 測試
        • 4.2.3 熔斷器全部配置
    • 5. 服務監控HystrixDashboard
      • 5.1 建立cloud-hystrix-dashboard9001子產品
        • 5.1.1 pom檔案
        • 5.1.2 yml檔案
        • 5.1.3 啟動類
      • 5.2 修改被監控項目cloud-provider-hystrix-payment8007
        • 5.2.1 pom檔案添加actuator依賴
        • 5.2.2 啟動類
      • 5.3 通路監控頁面

易于了解筆記:https://www.cnblogs.com/yawen/p/6655352.html

詳細概念流程:https://blog.csdn.net/loushuiyifan/article/details/82702522

1. Hystrix了解

1.1 概述

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

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

Hystrix作用:

  • 服務降級
  • 服務熔斷
  • 服務限流
  • 實時監控

1.2 背景

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

【SpringCloud】Hystrix斷路器【五】

1.3 雪崩效應常見場景

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

1.4 雪崩效應應對政策

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

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

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

1.5 初探Hystrix

Hystrix [hɪst’rɪks],中文含義是豪豬,因其背上長滿棘刺,進而擁有了自我保護的能力。本文所說的Hystrix是Netflix開源的一款容錯架構,同樣具有自我保護能力。為了實作容錯和自我保護,下面我們看看Hystrix如何設計和實作的。

Hystrix設計目标:

  • 對來自依賴的延遲和故障進行防護和控制——這些- - 依賴通常都是通過網絡通路的
  • 阻止故障的連鎖反應
  • 快速失敗并迅速恢複
  • 回退并優雅降級
  • 提供近實時的監控與告警

Hystrix遵循的設計原則:

  • 防止任何單獨的依賴耗盡資源(線程)
  • 過載立即切斷并快速失敗,防止排隊
  • 盡可能提供回退以保護使用者免受故障
  • 使用隔離技術(例如隔闆,泳道和斷路器模式)來限制任何一個依賴的影響
  • 通過近實時的名額,監控和告警,確定故障被及時發現
  • 通過動态修改配置屬性,確定故障及時恢複
  • 防止整個依賴用戶端執行失敗,而不僅僅是網絡通信

Hystrix如何實作這些設計目标?

  • 使用指令模式将所有對外部服務(或依賴關系)的調用包裝在HystrixCommand或HystrixObservableCommand對象中,并将該對象放在單獨的線程中執行;
  • 每個依賴都維護着一個線程池(或信号量),線程池被耗盡則拒絕請求(而不是讓請求排隊)。
  • 記錄請求成功,失敗,逾時和線程拒絕。
  • 服務錯誤百分比超過了門檻值,熔斷器開關自動打開,一段時間内停止對該服務的所有請求。
  • 請求失敗,被拒絕,逾時或熔斷時執行降級邏輯。
  • 近實時地監控名額和配置的修改。

1.6 Hystrix流程圖

【SpringCloud】Hystrix斷路器【五】
【SpringCloud】Hystrix斷路器【五】

2. 建構問題項目

2.2 建構cloud-provider-hystrix-payment8007項目

2.2.1 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.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-provider-hystrix-payment8007</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-client</artifactId>
        </dependency>
        <!--web-->
        <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><!-- 引入自己定義的api通用包,可以使用Payment支付Entity -->
            <groupId>com.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </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>
           

2.2.2 yml檔案

server:
  port: 8007

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

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka

           

2.2.3 啟動類

package com.springcloud;

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

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

           

2.2.4 控制器

package com.springcloud.controller;

import org.omg.CORBA.TIMEOUT;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

@RestController
public class PaymentController {

    @GetMapping("payment/hystrix/ok")
    public String ok(){
        return "線程池:"+Thread.currentThread().getName()+",資訊:OK";
    }

    @GetMapping("payment/hystrix/timeout")
    public String timeout(){
        long timeout=3000;
        try {
            Thread.sleep(timeout);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "線程池:"+Thread.currentThread().getName()+",資訊:TIMEOUT ";
    }
}

           

2.2.5 普通測試

  1. 啟動7001
  2. 啟動8007
  3. 通路http://localhost:8007/payment/hystrix/ok
  4. 通路http://localhost:8007/payment/hystrix/timeout
    【SpringCloud】Hystrix斷路器【五】
    【SpringCloud】Hystrix斷路器【五】

2.2.6 高并發測試

  1. 對http://localhost:8007/payment/hystrix/ok進行高并發
  2. 通路ok頁面(卡頓)
  3. 通路timeout頁面(卡頓)

兩個頁面都卡頓的原因是并發量超過了Tomcat的工作線程數,沒有多餘的線程來分解壓力和處理。

【SpringCloud】Hystrix斷路器【五】
【SpringCloud】Hystrix斷路器【五】
【SpringCloud】Hystrix斷路器【五】
【SpringCloud】Hystrix斷路器【五】

2.3 建構cloud-consumer-hystrix-order6007項目

2.3.1 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.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-consumer-hystrix-order6007</artifactId>

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

        <!--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-client</artifactId>
        </dependency>
        <!--web-->
        <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><!-- 引入自己定義的api通用包,可以使用Payment支付Entity -->
            <groupId>com.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </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>
           

2.3.2 yml檔案

server:
  port: 6007

spring:
  application:
    name: cloud-consumer-hystrix-order

eureka:
  client:
    register-with-eureka: false
    fetch-registry: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka

           

2.3.3 啟動類

package com.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;

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

           

2.3.4 Feign的Service接口

package com.springcloud.service;

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

@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT")
public interface PaymentFeignService {
    @GetMapping("/payment/hystrix/ok")
    public String ok();

    @GetMapping("/payment/hystrix/timeout")
    public String timeout();
}

           

2.3.5 控制器

package com.springcloud.controller;

import com.springcloud.service.PaymentFeignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {
    @Autowired
    PaymentFeignService paymentFeignService;

    @GetMapping("/consumer/payment/hystrix/ok")
    public String ok(){
        return paymentFeignService.ok();
    }

    @GetMapping("/consumer/payment/hystrix/timeout")
    public String timeout(){
        return paymentFeignService.timeout();
    }
}

           

2.3.6 普通測試

  1. 啟動7001
  2. 啟動8007
  3. 啟動6007
  4. 通路http://localhost:6007/consumer/payment/hystrix/ok
  5. 通路http://localhost:6007/consumer/payment/hystrix/timeout
【SpringCloud】Hystrix斷路器【五】
【SpringCloud】Hystrix斷路器【五】

3.2.7 對8007高并發測試

繼續對8007高并發測試,因為6007調用了8007的服務,但是8007因為被大量并發夾住了,導緻6007也卡住了,最終導緻使用者體驗不好

【SpringCloud】Hystrix斷路器【五】
【SpringCloud】Hystrix斷路器【五】

3 服務降級

3.1 概述

當服務出現異常狀況時不讓用戶端等待并立刻傳回一個友好提示fallback

哪些情況會觸發降級

  • 程式運作異常
  • 逾時
  • 服務熔斷觸發服務降級
  • 線程池/信号量打滿也會導緻服務降級

以8007和6007作為例子

  • 對方服務(8007)逾時或down機了,調用者(6007)不能一直卡死等待,必須有服務降級
  • 對方服務(8007)OK,調用者(6007)自己出故障或有自我要求(自己的等待時間小于服務提供者),自己處理降級

3.2 服務提供者8007服務降級(逾時)

3.2.1 啟動類

增加@EnableCircuitBreaker或@EnableHystrix注解,@EnableHystrix繼承擴充了@EnableCircuitBreaker的功能

package com.springcloud;

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

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

           

3.2.2 控制器類

使用@HystrixCommand注解添加到需要服務降級的方法上

package com.springcloud.controller;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import lombok.extern.slf4j.Slf4j;
import org.omg.CORBA.TIMEOUT;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

@RestController
@Slf4j
public class PaymentController {

    @GetMapping("/payment/hystrix/ok")
    public String ok(){
        String result="線程池:"+Thread.currentThread().getName()+",資訊:OK";
        log.info(result);
        return result;
    }

    // fallbackMethod是當timeout()出錯或逾時時調用timeout_handler()進行處理響應給調用者
    @HystrixCommand(fallbackMethod = "timeout_handler",commandProperties ={
            // execution.isolation.thread.timeoutInMilliseconds表示timeout()執行超過指定時間則觸發服務降級
            // 因為6007的Feign預設隻會等待1秒,是以這裡設定900毫秒,有足夠時間把timeout_handler()響應給6007
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "900")
    })
    @GetMapping("/payment/hystrix/timeout")
    public String timeout(){
        long timeout=3000;
        try {
            Thread.sleep(timeout);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        String result="線程池:"+Thread.currentThread().getName()+",資訊:TIMEOUT";
        log.info(result);
        return result;
    }

    public String timeout_handler(){
        String result="線程池:"+Thread.currentThread().getName()+",資訊:調用8007支付接口逾時或異常 ";
        return result;
    }
}

           

3.2.3 測試

  1. 啟動7001
  2. 啟動8007
  3. 啟動6007
  4. 注意:修改@HystrixCommand中的屬性需要重新開機服務,自動部署devtools不一定生效
  5. 通路http://localhost:6007/consumer/payment/hystrix/timeout
【SpringCloud】Hystrix斷路器【五】

3.3 服務消費者6007服務降級(異常)

3.3.1 啟動類

增加@EnableCircuitBreaker或@EnableHystrix注解,@EnableHystrix繼承擴充了@EnableCircuitBreaker的功能

package com.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;

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

3.3.2 控制器

package com.springcloud.controller;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.springcloud.service.PaymentFeignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {
    @Autowired
    PaymentFeignService paymentFeignService;


    @HystrixCommand(fallbackMethod = "ok_handler")
    @GetMapping("/consumer/payment/hystrix/ok")
    public String ok(){
        int num=10/0;
        return paymentFeignService.ok();
    }

    public String ok_handler(){
        return "6007出錯了,請聯系管理者";
    }

    @GetMapping("/consumer/payment/hystrix/timeout")
    public String timeout(){
        return paymentFeignService.timeout();
    }
}

           

3.3.3 測試

  1. 啟動7001
  2. 啟動8007
  3. 啟動6007
  4. 注意:修改@HystrixCommand中的屬性需要重新開機服務,自動部署devtools不一定生效
  5. 通路http://localhost:6007/consumer/payment/hystrix/ok
【SpringCloud】Hystrix斷路器【五】

3.4 簡化代碼結構

3.4.1 預設Fallback方法

package com.springcloud.controller;

import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.springcloud.service.PaymentFeignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
// 指定預設的Fallback方法
@DefaultProperties(defaultFallback = "default_handler")
public class OrderController {
    @Autowired
    PaymentFeignService paymentFeignService;

    // 預設Fallback必須是無參的
    public String default_handler(){
        return "8007系統down機啦啦啦啦啦啦啦啦啦";
    }

    @HystrixCommand(fallbackMethod = "ok_handler")
    @GetMapping("/consumer/payment/hystrix/ok")
    public String ok(){
        int num=10/0;
        return paymentFeignService.ok();
    }

    public String ok_handler(){
        return "6007出錯了,請聯系管理者";
    }

    // @HystrixCommand如果沒有設定fallbackMethod屬性,則會自動使用預設Fallback
    @HystrixCommand  
    @GetMapping("/consumer/payment/hystrix/timeout")
    public String timeout(){
        return paymentFeignService.timeout();
    }
}

           

啟動7001

啟動6007

不要啟動8007

通路http://localhost:6007/consumer/payment/hystrix/ok

通路http://localhost:6007/consumer/payment/hystrix/timeout

【SpringCloud】Hystrix斷路器【五】
【SpringCloud】Hystrix斷路器【五】

3.4.2 OpenFeign與Hystrix合用

  • yml檔案
server:
  port: 6007

spring:
  application:
    name: cloud-consumer-hystrix-order

eureka:
  client:
    register-with-eureka: false
    fetch-registry: true
    service-url:
      defaultZone: http://eureka7001.com:7001/eureka

# OpenFeign與Hystrix何用,需要添加以下代碼
feign:
  hystrix:
    enabled: true

           
  • 啟動類
package com.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication
@EnableFeignClients  // 開啟OpenFeign支援
@EnableHystrix       // 開啟Hystrix支援
public class OrderHystrixMain6007 {
    public static void main(String[] args) {
        SpringApplication.run(OrderHystrixMain6007.class,args);
    }
}

           
  • Feign服務接口

設定@FeignClient的fallback屬性

package com.springcloud.service;

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

@Component
// 在@FeignClient注解的fallback屬性是為目前調用遠端服務的Feign接口進行服務降級Fallback處理的類
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentFallbackService.class)
public interface PaymentFeignService {
    @GetMapping("/payment/hystrix/ok")
    public String ok();

    @GetMapping("/payment/hystrix/timeout")
    public String timeout();
}

           
  • Fallback處理類
package com.springcloud.service;

import org.springframework.stereotype.Component;

@Component  //記得加上@Component注解
// 實作Feign服務的接口,為每個遠端通路的方法設定Fallback
public class PaymentFallbackService implements  PaymentFeignService {
    @Override
    public String ok() {
        return "8007的ok出錯了";
    }

    @Override
    public String timeout() {
        return "8007的timeout出錯了";
    }

}

           
  • 控制器

還原成原來的樣子

package com.springcloud.controller;

import com.springcloud.service.PaymentFeignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class OrderController {
    @Autowired
    PaymentFeignService paymentFeignService;

    @GetMapping("/consumer/payment/hystrix/ok")
    public String ok(){
        return paymentFeignService.ok();
    }

    @GetMapping("/consumer/payment/hystrix/timeout")
    public String timeout(){
        return paymentFeignService.timeout();
    }
}

           
  • 測試

啟動7001

啟動6007

不要啟動8007

通路http://localhost:6007/consumer/payment/hystrix/ok

通路http://localhost:6007/consumer/payment/hystrix/timeout

【SpringCloud】Hystrix斷路器【五】
【SpringCloud】Hystrix斷路器【五】

4. 服務熔斷

4.1 概述

4.1.1 熔斷器是什麼

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

4.1.2 熔斷器狀态

閉合:不會對服務進行熔斷,即不幹活

斷開:請求不再進行調用目前服務,直接fallback

半開:斷開後每隔一段時間,嘗試調用目前服務,根據規則改變熔斷器狀态

4.1.3 執行過程

【SpringCloud】Hystrix斷路器【五】
  1. 當請求數滿足一定的閥值的時候(預設10秒内超過20個請求次數)熔斷器才會開始工作;如果請求數低于閥值,熔斷器不會幹活,即上面流程完全不執行。
  2. 熔斷器一開始是閉合狀态,如果一定時間内超過規定請求失敗率(預設10秒内50%的失敗率)熔斷器就會斷開;如果低于失敗率則正常通路目前服務。
  3. 熔斷器斷開期間(預設5秒)所有請求直接進入fallback。
  4. 熔斷器斷開指定時間(預設5秒)後會進入半開狀态,會讓其中一個請求通路目前服務,如果成功則把熔斷器閉合,如果失敗則把熔斷器斷開。
  5. 根據熔斷器狀态重複以上步驟

4.2 案例示範

為了省事在8007建立一個HystrixController,直接在浏覽器通路HystrixController中的方法,不再用6007調用8007的服務

4.2.1 HystrixController

package com.springcloud.controller;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HystrixController {

    @HystrixCommand(fallbackMethod = "division_handler",commandProperties = {
            @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),  //啟用熔斷器
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), //快照時間窗
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "20"),  //在快照時間窗内請求總數閥值
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "50"),//在快照時間窗内請求失敗百分比閥值
    })
    @GetMapping("/hystrix/division/{num}")
    public String division(@PathVariable("num") int num){
        int result=10/num;
        return "結果:"+result;
    }

    public String division_handler(@PathVariable("num") int num){
        return "出錯啦,數字:"+num;
    }
}
           

4.2.2 測試

單次通路
【SpringCloud】Hystrix斷路器【五】
【SpringCloud】Hystrix斷路器【五】

過程:先10秒内錯誤通路5次,10秒後正确通路1次

結果:10秒後通路那一次正确顯示結果

原因:10秒内通路5次,未達到讓熔斷器幹活的程度

過程:先10秒内錯誤通路20次以上,第10秒正确通路1次,第15秒正确通路1次,第20秒正确通路1次

結果:10秒後通路那一次顯示出錯了,第15秒通路顯示成功,第20秒通路成功

原因:10秒内通路超過20次,熔斷器開始幹活了,因為在這10秒内全是錯誤通路,失敗率達到了100%,遠超過規定的50%,是以熔斷器斷開了,斷開 的期間(預設持續5秒),所有請求都是直接調用fallback,是以雖然是正确通路,但結果是錯誤的;熔斷器斷開5秒後進入半開狀态,嘗試恢複鍊路,對第15秒通路目前服務,通路成功了;第15秒通路成功會把熔斷器從半開切換成閉合狀态,是以第20秒也能正常通路(未到熔斷器幹活次數)

4.2.3 熔斷器全部配置

//========================All
@HystrixCommand(fallbackMethod = "str_fallbackMethod",
 groupKey = "strGroupCommand",
    commandKey = "strCommand",
    threadPoolKey = "strThreadPool",

    commandProperties = {
            // 設定隔離政策,THREAD 表示線程池 SEMAPHORE:信号池隔離
            @HystrixProperty(name = "execution.isolation.strategy", value = "THREAD"),
            // 當隔離政策選擇信号池隔離的時候,用來設定信号池的大小(最大并發數)
            @HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "10"),
            // 配置指令執行的逾時時間
            @HystrixProperty(name = "execution.isolation.thread.timeoutinMilliseconds", value = "10"),
            // 是否啟用逾時時間
            @HystrixProperty(name = "execution.timeout.enabled", value = "true"),
            // 執行逾時的時候是否中斷
            @HystrixProperty(name = "execution.isolation.thread.interruptOnTimeout", value = "true"),
            // 執行被取消的時候是否中斷
            @HystrixProperty(name = "execution.isolation.thread.interruptOnCancel", value = "true"),
            // 允許回調方法執行的最大并發數
            @HystrixProperty(name = "fallback.isolation.semaphore.maxConcurrentRequests", value = "10"),
            // 服務降級是否啟用,是否執行回調函數
            @HystrixProperty(name = "fallback.enabled", value = "true"),
            // 是否啟用斷路器
            @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
            // 該屬性用來設定在滾動時間窗中,斷路器熔斷的最小請求數。例如,預設該值為 20 的時候,
            // 如果滾動時間窗(預設10秒)内僅收到了19個請求, 即使這19個請求都失敗了,斷路器也不會打開。
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),
            // 該屬性用來設定在滾動時間窗中,表示在滾動時間窗中,在請求數量超過
            // circuitBreaker.requestVolumeThreshold 的情況下,如果錯誤請求數的百分比超過50,
            // 就把斷路器設定為 "打開" 狀态,否則就設定為 "關閉" 狀态。
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
            // 該屬性用來設定當斷路器打開之後的休眠時間窗。 休眠時間窗結束之後,
            // 會将斷路器置為 "半開" 狀态,嘗試熔斷的請求指令,如果依然失敗就将斷路器繼續設定為 "打開" 狀态,
            // 如果成功就設定為 "關閉" 狀态。
            @HystrixProperty(name = "circuitBreaker.sleepWindowinMilliseconds", value = "5000"),
            // 斷路器強制打開
            @HystrixProperty(name = "circuitBreaker.forceOpen", value = "false"),
            // 斷路器強制關閉
            @HystrixProperty(name = "circuitBreaker.forceClosed", value = "false"),
            // 滾動時間窗設定,該時間用于斷路器判斷健康度時需要收集資訊的持續時間
            @HystrixProperty(name = "metrics.rollingStats.timeinMilliseconds", value = "10000"),
            // 該屬性用來設定滾動時間窗統計名額資訊時劃分"桶"的數量,斷路器在收集名額資訊的時候會根據
            // 設定的時間窗長度拆分成多個 "桶" 來累計各路徑成本,每個"桶"記錄了一段時間内的采集名額。
            // 比如 10 秒内拆分成 10 個"桶"收集這樣,是以 timeinMilliseconds 必須能被 numBuckets 整除。否則會抛異常
            @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "10"),
            // 該屬性用來設定對指令執行的延遲是否使用百分位數來跟蹤和計算。如果設定為 false, 那麼所有的概要統計都将傳回 -1。
            @HystrixProperty(name = "metrics.rollingPercentile.enabled", value = "false"),
            // 該屬性用來設定百分位統計的滾動視窗的持續時間,機關為毫秒。
            @HystrixProperty(name = "metrics.rollingPercentile.timeInMilliseconds", value = "60000"),
            // 該屬性用來設定百分位統計滾動視窗中使用 “ 桶 ”的數量。
            @HystrixProperty(name = "metrics.rollingPercentile.numBuckets", value = "60000"),
            // 該屬性用來設定在執行過程中每個 “桶” 中保留的最大執行次數。如果在滾動時間窗内發生超過該設定值的執行次數,
            // 就從最初的位置開始重寫。例如,将該值設定為100, 滾動視窗為10秒,若在10秒内一個 “桶 ”中發生了500次執行,
            // 那麼該 “桶” 中隻保留 最後的100次執行的統計。另外,增加該值的大小将會增加記憶體量的消耗,并增加排序百分位數所需的計算時間。
            @HystrixProperty(name = "metrics.rollingPercentile.bucketSize", value = "100"),
            // 該屬性用來設定采集影響斷路器狀态的健康快照(請求的成功、 錯誤百分比)的間隔等待時間。
            @HystrixProperty(name = "metrics.healthSnapshot.intervalinMilliseconds", value = "500"),
            // 是否開啟請求緩存
            @HystrixProperty(name = "requestCache.enabled", value = "true"),
            // HystrixCommand的執行和事件是否列印日志到 HystrixRequestLog 中
            @HystrixProperty(name = "requestLog.enabled", value = "true"),
    },
    threadPoolProperties = {
            // 該參數用來設定執行指令線程池的核心線程數,該值也就是指令執行的最大并發量
            @HystrixProperty(name = "coreSize", value = "10"),
            // 該參數用來設定線程池的最大隊列大小。當設定為 -1 時,線程池将使用 SynchronousQueue 實作的隊列,
            // 否則将使用 LinkedBlockingQueue 實作的隊列。
            @HystrixProperty(name = "maxQueueSize", value = "-1"),
            // 該參數用來為隊列設定拒絕門檻值。 通過該參數, 即使隊列沒有達到最大值也能拒絕請求。
            // 該參數主要是對 LinkedBlockingQueue 隊列的補充,因為 LinkedBlockingQueue
            // 隊列不能動态修改它的對象大小,而通過該屬性就可以調整拒絕請求的隊列大小了。
            @HystrixProperty(name = "queueSizeRejectionThreshold", value = "5"),
    }
)
public String strConsumer() {
    return "hello 2020";
}
public String str_fallbackMethod()
{
    return "*****fall back str_fallbackMethod";
}
 


           

5. 服務監控HystrixDashboard

5.1 建立cloud-hystrix-dashboard9001子產品

5.1.1 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.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-hystrix-dashboard9001</artifactId>

    <dependencies>
        <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>

           

5.1.2 yml檔案

server:
  port: 9001
           

5.1.3 啟動類

package com.springcloud;

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

@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardMain9001 {
    public static void main(String[] args) {
        SpringApplication.run(HystrixDashboardMain9001.class,args);
    }
}

           

5.2 修改被監控項目cloud-provider-hystrix-payment8007

5.2.1 pom檔案添加actuator依賴

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

5.2.2 啟動類

package com.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.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.context.annotation.Bean;

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

    /**
     *此配置是為了服務監控而配置,與服務容錯本身無關,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;
    }

}

           

5.3 通路監控頁面

【SpringCloud】Hystrix斷路器【五】
【SpringCloud】Hystrix斷路器【五】
【SpringCloud】Hystrix斷路器【五】