欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringCloud?Hystrix?斷路器的實(shí)現(xiàn)

 更新時(shí)間:2025年03月11日 14:28:22   作者:快樂的小三菊  
本文主要介紹了SpringCloud?Hystrix?斷路器的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

一、前言

接下來是開展一系列的 SpringCloud 的學(xué)習(xí)之旅,從傳統(tǒng)的模塊之間調(diào)用,一步步的升級為 SpringCloud 模塊之間的調(diào)用,此篇文章為第五篇,即介紹 Hystrix 斷路器。

二、概述

2.1 分布式系統(tǒng)面臨的問題

復(fù)雜分布式體系結(jié)構(gòu)中的應(yīng)用程序有數(shù)十個(gè)依賴關(guān)系,每個(gè)依賴關(guān)系在某些時(shí)候?qū)⒉豢杀苊獾厥 ?/p>

下圖中的請求需要調(diào)用 A、PH、四個(gè)服務(wù),如果一切順利則沒有什么問題,關(guān)鍵是如果 I 服務(wù)超時(shí)會(huì)出現(xiàn)什么情況呢?

會(huì)出現(xiàn)服務(wù)雪崩的現(xiàn)象。多個(gè)微服務(wù)之間調(diào)用的時(shí)候,假設(shè)微服務(wù) A 調(diào)用微服務(wù) B 和微服務(wù) C,微服務(wù) B 和微服務(wù) C 又調(diào)用其它的微服務(wù),這就是所謂的“扇出”。如果扇出的鏈路上某個(gè)微服務(wù)的調(diào)用響應(yīng)時(shí)間過長或者不可用,對微服務(wù) A 的調(diào)用就會(huì)占用越來越多的系統(tǒng)資源,進(jìn)而引起系統(tǒng)崩潰,所謂的“雪崩效應(yīng)” 。

對于高流量的應(yīng)用來說,單一的后端依賴可能會(huì)導(dǎo)致所有服務(wù)器上的所有資源都在幾秒鐘內(nèi)飽和。比失敗更糟糕的是,這些應(yīng)用程序還可能導(dǎo)致服務(wù)之間的延遲增加,備份隊(duì)列,線程和其他系統(tǒng)資源緊張,導(dǎo)致整個(gè)系統(tǒng)發(fā)生更多的級聯(lián)故障。這些都表示需要對故障和延遲進(jìn)行隔離和管理,以便單個(gè)依賴關(guān)系的失敗,不能取消整個(gè)應(yīng)用程序或系統(tǒng)。

所以,通常當(dāng)你發(fā)現(xiàn)一個(gè)模塊下的某個(gè)實(shí)例失敗后,這時(shí)候這個(gè)模塊依然還會(huì)接收流量,然后這個(gè)有問題的模塊還調(diào)用了其他的模塊,這樣就會(huì)發(fā)生級聯(lián)故障,或者叫雪崩。

2.2 Hystrix 是什么

Hystrix 是一個(gè)用于處理分布式系統(tǒng)的延遲和容錯(cuò)的開源庫,在分布式系統(tǒng)里,許多依賴不可避免的會(huì)調(diào)用失敗,比如超時(shí)、異常等,Hystrix 能夠保證在一個(gè)依賴出問題的情況下,不會(huì)導(dǎo)致整體服務(wù)失敗,避免級聯(lián)故障,以提高分布式系統(tǒng)的彈性。

“斷路器”本身是一種開關(guān)裝置,當(dāng)某個(gè)服務(wù)單元發(fā)生故障之后,通過斷路器的故障監(jiān)控(類似熔斷保險(xiǎn)絲),向調(diào)用方返回一個(gè)符合預(yù)期的、可處理的備選響應(yīng)(FallBack),而不是長時(shí)間的等待或者拋出調(diào)用方無法處理的異常,這樣就保證了服務(wù)調(diào)用方的線程不會(huì)被長時(shí)間、不必要地占用,從而避免了故障在分布式系統(tǒng)中的蔓延,乃至雪崩。

2.3 Hystrix 用途

1、服務(wù)降級

2、服務(wù)熔斷

3、接近實(shí)時(shí)的監(jiān)控

2.4 現(xiàn)狀

目前 Hystrix 也已經(jīng)進(jìn)入了維護(hù)期。如下圖:

三、Hystrix 重要概念

3.1 服務(wù)降級

當(dāng)客戶端遲遲得不到服務(wù)端的響應(yīng)時(shí),給客戶端提示一個(gè) “服務(wù)器忙,請稍后再試” 類似的響應(yīng),不讓客戶端等待并立刻返回一個(gè)友好提示。

哪些情況會(huì)觸發(fā)降級?程序運(yùn)行異常、超時(shí)、服務(wù)熔斷觸發(fā)服務(wù)降級、線程池/信號(hào)量打滿等也會(huì)觸發(fā)服務(wù)降級。

3.2 服務(wù)熔斷

和家里以前用的保險(xiǎn)絲類似,當(dāng)服務(wù)器達(dá)到最大服務(wù)訪問后,直接拒絕訪問,拉閘限電,然后調(diào)用服務(wù)降級的方法返回友好提示。

服務(wù)降級  --> 進(jìn)而熔斷  --> 恢復(fù)鏈路調(diào)用。

3.3 服務(wù)限流

當(dāng)出現(xiàn)類似于秒殺等高并發(fā)操作,嚴(yán)禁一窩蜂的過來擁擠,大家排隊(duì),一秒鐘 個(gè),有序進(jìn)行。

四、Hystrix 案例

4.1 構(gòu)建

創(chuàng)建一個(gè)子模塊工程 cloud-provider-hystrix-payment8001pom.xml 內(nèi)容如下:

<?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">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.springcloud</groupId>
        <artifactId>SpringCloud</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

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

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <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>

為了方便后續(xù)的測試,我們這邊先不采用集群的方式進(jìn)行配置,只配置一臺(tái)服務(wù)進(jìn)行注冊,application.yml 內(nèi)容如下所示:

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
      defaultZone: http://eureka7001.com:7001/eureka

主啟動(dòng)類的代碼如下所示:

package com.springcloud;

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

@SpringBootApplication
@EnableEurekaClient //本服務(wù)啟動(dòng)后會(huì)自動(dòng)注冊進(jìn)eureka服務(wù)中
public class PaymentHystrixMain8001
{
    public static void main(String[] args)
    {
        SpringApplication.run(PaymentHystrixMain8001.class,args);
    }
}

業(yè)務(wù)類 service 層代碼如下所示:

@Service
public class PaymentService
{
    /**
     * 正常訪問,一切OK
     */
    public String paymentInfo_OK(Integer id)
    {
        return "線程池:"+Thread.currentThread().getName()+"paymentInfo_OK,id: "+id+"\t"+"O(∩_∩)O";
    }

    /**
     * 超時(shí)訪問,演示降級
     */
    public String paymentInfo_TimeOut(Integer id)
    {
        try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
        return "線程池:"+Thread.currentThread().getName()+"paymentInfo_TimeOut,id: "+id+"\t"+"O(∩_∩)O,耗費(fèi)3秒";
    }
}

控制層 controller 代碼如下所示: 

@RestController
@Slf4j
public class PaymentController
{
    @Autowired
    private PaymentService paymentService;

    @Value("${server.port}")
    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(@PathVariable("id") Integer id) throws InterruptedException
    {
        String result = paymentService.paymentInfo_TimeOut(id);
        log.info("****result: "+result);
        return result;
    }
}

分別啟動(dòng) cloud-eureka-server7001 和 cloud-provider-hystrix-payment8001 模塊進(jìn)行方法測試,訪問 http://localhost:8001/payment/hystrix/ok/31,效果如下:

當(dāng)問 http://localhost:8001/payment/hystrix/timeout/31,效果如下:

可以看到,這兩個(gè)方法都可以正常的訪問。

4.2 高并發(fā)測試

4.2.1 模塊自測

上面的兩個(gè)方法在非高并發(fā)的情形下,訪問時(shí)沒有任何問題的,接下來我們測試下在高并發(fā)的場景下方法還是否可以正常訪問。

啟動(dòng) jemeter,如果你沒有用過這個(gè)壓測軟件,請參考安裝使用的這兩篇文章,創(chuàng)建 2000 個(gè)并發(fā)請求對 cloud-provider-hystrix-payment8001 模塊進(jìn)行壓測,使這 2000 個(gè)請求都去訪問 paymentInfo_TimeOut 服務(wù),如下圖:

然后再來一個(gè)請求去訪問 paymentInfo_OK,如下圖,

可以看到,這兩個(gè)請求都在轉(zhuǎn)圈圈,都得等待一會(huì)才能夠響應(yīng),這是因?yàn)?nbsp;tomcat 的默認(rèn)的工作線程數(shù)被打滿了,沒有多余的線程來分解壓力和處理。

上面還是服務(wù)提供者 8001 自己測試,假如此時(shí)外部的消費(fèi)者 80 也來訪問,那消費(fèi)者只能干等,最終導(dǎo)致消費(fèi)端 80 不滿意,服務(wù)端 8001 直接被拖死。接下來我們驗(yàn)證下。

4.2.2 模塊公測

新建一個(gè)訂單模塊 cloud-consumer-feign-hystrix-order80pom.xml 內(nèi)容如下所示:

<?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">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.springcloud</groupId>
        <artifactId>SpringCloud</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

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

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <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.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>${project.version}</version>
        </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>
        <!--一般基礎(chǔ)通用配置-->
        <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>

application.yml 的內(nèi)容如下所示:

server:
  port: 80

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

主啟動(dòng)類的代碼如下所示:

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

service 層代碼如下所示:

@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT")
public interface PaymentHystrixService
{
    @GetMapping("/payment/hystrix/ok/{id}")
    String paymentInfo_OK(@PathVariable("id") Integer id);

    @GetMapping("/payment/hystrix/timeout/{id}")
    String paymentInfo_TimeOut(@PathVariable("id") Integer id);
}

controller 層代碼如下所示:

@RestController
@Slf4j
public class OrderHystirxController {
    @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;
    }
}

啟動(dòng) cloud-consumer-feign-hystrix-order80 模塊進(jìn)行測試,輸入:http://localhost/consumer/payment/hystrix/ok/31,如下,可以感覺到響應(yīng)很快。

接下來進(jìn)行高并發(fā)測試,啟動(dòng) jmeter ,使用 20000 個(gè)線程壓測 cloud-provider-hystrix-payment8001 模塊,并讓 order80 微服務(wù)再去訪問正常的 Ok 微服務(wù) 8001 地址,http://localhost/consumer/payment/hystrix/ok/32,如下:

如上圖可以看到,order80 要么轉(zhuǎn)圈圈等待,要么消費(fèi)端報(bào)超時(shí)錯(cuò)誤。

4.3 故障現(xiàn)象和原因

cloud-provider-hystrix-payment8001 模塊的同一層次的其它接口服務(wù)被困死,因?yàn)?nbsp;tomcat 線程池里面的工作線程已經(jīng)被擠占完畢,order80  此時(shí)調(diào)用 8001,客戶端訪問響應(yīng)緩慢,轉(zhuǎn)圈圈。

4.4 上述結(jié)論

正因?yàn)橛猩鲜龉收匣虿患驯憩F(xiàn),才有我們的降級/容錯(cuò)/限流等技術(shù)誕生。

4.5 解決方案

1、若超時(shí)導(dǎo)致服務(wù)器變慢,出現(xiàn)轉(zhuǎn)圈現(xiàn)象,則超時(shí)不再等待。

2、若出現(xiàn)宕機(jī)或者程序運(yùn)行出錯(cuò),則要有兜底的方案。

3、若對方服務(wù) (8001) 超時(shí)了,調(diào)用者(order80)不能一直卡死等待,必須有服務(wù)降級。

4、若對方服務(wù) (8001down 機(jī)了,調(diào)用者 (80) 不能一直卡死等待,必須有服務(wù)降級。

5、若對方服務(wù) (8001OK,調(diào)用者 (80) 自己出故障或有自我要求(自己的等待時(shí)間小于服務(wù)提供者),自己處理降級。

4.6 服務(wù)降級

首先從 cloud-provider-hystrix-payment8001 模塊自身找問題,設(shè)置自身調(diào)用超時(shí)時(shí)間的峰值,峰值內(nèi)可以正常運(yùn)行,超過了需要有兜底的方法處理,作服務(wù)降級 fallback。

4.6.1 服務(wù)端降級

首先修改 cloud-provider-hystrix-payment8001 模塊的 PaymentHystrixService 類,代碼如下:

@Service
public class PaymentService
{
    /**
     * 正常訪問,一切OK
     */
    public String paymentInfo_OK(Integer id)
    {
        return "線程池:"+Thread.currentThread().getName()+"paymentInfo_OK,id: "+id+"\t"+"O(∩_∩)O";
    }

    /**
     * 超時(shí)訪問,演示降級
     */
    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="5000")
    })
    public String paymentInfo_TimeOut(Integer id)
    {
        try { TimeUnit.SECONDS.sleep(7); } catch (InterruptedException e) { e.printStackTrace(); }
        return "線程池:"+Thread.currentThread().getName()+"paymentInfo_TimeOut,id: "+id+"\t"+"O(∩_∩)O,耗費(fèi)3秒";
    }
    public String paymentInfo_TimeOutHandler(Integer id){
        return "/(ㄒoㄒ)/調(diào)用支付接口超時(shí)或異常:\t"+ "\t當(dāng)前線程池名字" + Thread.currentThread().getName();
    }
}

一旦調(diào)用服務(wù)方法失敗并拋出了錯(cuò)誤信息后,會(huì)自動(dòng)調(diào)用 @HystrixCommand 標(biāo)注好的 fallbackMethod 調(diào)用類中的指定方法。

在主啟動(dòng)類上添加 @EnableCircuitBreaker 注解,如下圖:

@SpringBootApplication
@EnableEurekaClient //本服務(wù)啟動(dòng)后會(huì)自動(dòng)注冊進(jìn)eureka服務(wù)中
@EnableCircuitBreaker
public class PaymentHystrixMain8001
{
    public static void main(String[] args)
    {
        SpringApplication.run(PaymentHystrixMain8001.class,args);
    }
}

啟動(dòng)模塊進(jìn)行測試,由于我們配置的是超時(shí)時(shí)間為 5s,而程序需要處理 7s,程序調(diào)用時(shí)應(yīng)該會(huì)調(diào)用我們指定的方法,如下圖:

繼續(xù)修改下 PaymentHystrixService 類的代碼,演示下當(dāng)程序報(bào)錯(cuò)時(shí),是否會(huì)調(diào)用我們指定的方法,如下:

@Service
public class PaymentService
{
    /**
     * 正常訪問,一切OK
     */
    public String paymentInfo_OK(Integer id)
    {
        return "線程池:"+Thread.currentThread().getName()+"paymentInfo_OK,id: "+id+"\t"+"O(∩_∩)O";
    }

    /**
     * 超時(shí)訪問,演示降級
     */
    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler",commandProperties = {
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="5000")
    })
    public String paymentInfo_TimeOut(Integer id)
    {
            int a = 10/0;
        // try { TimeUnit.SECONDS.sleep(7); } catch (InterruptedException e) { e.printStackTrace(); }
        return "線程池:"+Thread.currentThread().getName()+"paymentInfo_TimeOut,id: "+id+"\t"+"O(∩_∩)O,耗費(fèi)3秒";
    }
    public String paymentInfo_TimeOutHandler(Integer id){
        return "/(ㄒoㄒ)/調(diào)用支付接口超時(shí)或異常:\t"+ "\t當(dāng)前線程池名字" + Thread.currentThread().getName();
    }
}

可以看到,當(dāng)程序發(fā)生錯(cuò)誤時(shí),也會(huì)進(jìn)入我們指定的方法

總結(jié):當(dāng)前服務(wù)超時(shí)或內(nèi)部錯(cuò)誤時(shí),都做服務(wù)降級,兜底的方案都是  paymentInfo_TimeOutHandler

4.6.2 客戶端降級

修改 cloud-consumer-fiegn-hystrix-order80 模塊的 application.yml,內(nèi)容如下:

server:
  port: 80

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

feign:
  hystrix:
    enabled: true

在主啟動(dòng)類上添加 @EnableHystrix 注解,代碼如下:

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

修改 OrderHystirxController 類代碼,如下:

@RestController
@Slf4j
public class OrderHystirxController {
    @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}")
    @HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod",commandProperties = {
            @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="1500")
    })
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id)
    {
        String result = paymentHystrixService.paymentInfo_TimeOut(id);
        return result;
    }
    public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id)
    {
        return "我是消費(fèi)者80,對方支付系統(tǒng)繁忙請10秒鐘后再試或者自己運(yùn)行出錯(cuò)請檢查自己,o(╥﹏╥)o";
    }
}

啟動(dòng)  cloud-consumer-fiegn-hystrix-order80 模塊進(jìn)行測試,如下,可以看到客戶端的服務(wù)降級也成功了。

4.6.3 目前問題

1、每個(gè)業(yè)務(wù)方法都需要寫一個(gè)兜底的方法,代碼膨脹。

2、業(yè)務(wù)代碼和兜底方法放在一起,混亂不堪。

4.6.4 配置全局方法

為了解決每個(gè)業(yè)務(wù)方法都需要寫一個(gè)兜底的方法,代碼膨脹的問題,可以采取配置全局服務(wù)降級的方法。

此種方法可以解決出現(xiàn)運(yùn)行時(shí)異常和超時(shí)異常的服務(wù)降級。

修改 cloud-consumer-fiegn-hystrix-order80 模塊的 OrderHystirxController 類的代碼如下所示:

@RestController
@Slf4j
@DefaultProperties(defaultFallback = "payment_Global_FallbackMethod")
public class OrderHystirxController {
    @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}")
    // 加了@DefaultProperties屬性注解,并且沒有寫具體方法名字,就用統(tǒng)一全局的
    @HystrixCommand
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id)
    {
        int a = 10/0;
        String result = paymentHystrixService.paymentInfo_TimeOut(id);
        return result;
    }
    public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id)
    {
        return "我是消費(fèi)者80,對方支付系統(tǒng)繁忙請10秒鐘后再試或者自己運(yùn)行出錯(cuò)請檢查自己,o(╥﹏╥)o";
    }
    public String payment_Global_FallbackMethod()
    {
        return "Global異常處理信息,請稍后再試,/(ㄒoㄒ)/~~";
    }
}

通過添加 @DefaultProperties(defaultFallback = "") 注解,統(tǒng)一跳轉(zhuǎn)到統(tǒng)一處理結(jié)果頁面

如果有個(gè)別重要核心業(yè)務(wù)有專屬配套方法,只需要在該方法上繼續(xù)使用 @HystrixCommand 注解并指定拖地方法即可。

通用的和獨(dú)享的各自分開,避免了代碼膨脹,合理減少了代碼量。

4.6.5 解決服務(wù)端宕機(jī)

為了解決業(yè)務(wù)代碼和兜底方法放在一起,混亂不堪的問題。也是為了實(shí)現(xiàn)當(dāng)服務(wù)器宕機(jī)或關(guān)閉時(shí)的服務(wù)降級。

本次案例服務(wù)降級處理是在客戶端 order80 實(shí)現(xiàn)完成的,與服務(wù)端 8001 沒有關(guān)系,只需要為 Feign 客戶端定義的接口添加一個(gè)服務(wù)降級處理的實(shí)現(xiàn)類即可實(shí)現(xiàn)解耦。

未來我們要面對的異常包括:運(yùn)行時(shí)異常、超時(shí)異常和宕機(jī)異常。

修改 cloud-consumer-feign-hystrix-order80 模塊,根據(jù) cloud-consumer-feign-hystrix-order80 已經(jīng)有的 PaymentHystrixService 接口,重新新建一個(gè)類 PaymentFallbackService 實(shí)現(xiàn)該接口,統(tǒng)一為接口里面的方法進(jìn)行異常處理,代碼如下:

@Component
public class PaymentFallbackService implements PaymentHystrixService{
    @Override
    public String paymentInfo_OK(Integer id) {
        return "------- PaymentFallbackService  back-paymentInfo_OK  fall";
    }

    @Override
    public String paymentInfo_TimeOut(Integer id) {
        return  "------- PaymentFallbackService  back-paymentInfo_TimeOut  fall";
    }
}

修改 PaymentFeignClientService 類的代碼,添加 fallback,代碼如下:

@Component
@FeignClient(value = "CLOUD-PROVIDER-HYSTRIX-PAYMENT",fallback = PaymentFallbackService.class)
public interface PaymentHystrixService
{
    @GetMapping("/payment/hystrix/ok/{id}")
    String paymentInfo_OK(@PathVariable("id") Integer id);

    @GetMapping("/payment/hystrix/timeout/{id}")
    String paymentInfo_TimeOut(@PathVariable("id") Integer id);

}

分別啟動(dòng) cloud-eureka-server7001、 cloud-provider-hystrix-payment8001 和 cloud-consumer-feign-hystrix-order80 模塊,輸入 http://localhost/consumer/payment/hystrix/ok/32,進(jìn)行測試,如下圖:

此時(shí)故意關(guān)閉 cloud-provider-hystrix-payment8001 模塊,再次進(jìn)行訪問,如下圖:

此時(shí)服務(wù)端 provider 已經(jīng) down 了,但是我們做了服務(wù)降級處理,讓客戶端在服務(wù)端不可用時(shí)也會(huì)獲得提示信息而不會(huì)掛起耗死服務(wù)器。

4.7 服務(wù)熔斷

4.7.1 熔斷機(jī)制概述

熔斷機(jī)制是應(yīng)對雪崩效應(yīng)的一種微服務(wù)鏈路保護(hù)機(jī)制。當(dāng)扇出鏈路的某個(gè)微服務(wù)出錯(cuò)不可用或者響應(yīng)時(shí)間太長時(shí),會(huì)進(jìn)行服務(wù)的降級,進(jìn)而熔斷該節(jié)點(diǎn)微服務(wù)的調(diào)用,快速返回錯(cuò)誤的響應(yīng)信息。當(dāng)檢測到該節(jié)點(diǎn)微服務(wù)調(diào)用響應(yīng)正常后,恢復(fù)調(diào)用鏈路。

在 SpringCloud 框架里,熔斷機(jī)制通過 Hystrix 實(shí)現(xiàn)。Hystrix 會(huì)監(jiān)控微服務(wù)間調(diào)用的狀況,當(dāng)失敗的調(diào)用到一定閾值,缺省是 5 秒內(nèi) 20 次調(diào)用失敗,就會(huì)啟動(dòng)熔斷機(jī)制。熔斷機(jī)制的注解是 @HystrixCommand

4.7.2 熔斷實(shí)操

修改 cloud-provider-hystrix-payment8001 模塊的 PaymentService 類,添加如下的代碼:

    // =========服務(wù)熔斷====================
    @HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
            @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),// 是否開啟斷路器
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),// 請求次數(shù)
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"),// 時(shí)間窗口期
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"),// 失敗率達(dá)到多少后跳閘
    })
    })
    public String paymentCircuitBreaker(@PathVariable("id") Integer id)
    {
        if(id < 0)
        {
            throw new RuntimeException("******id 不能負(fù)數(shù)");
        }
        String serialNumber = IdUtil.simpleUUID();

        return Thread.currentThread().getName()+"\t"+"調(diào)用成功,流水號(hào): " + serialNumber;
    }
    public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id)
    {
        return "id 不能負(fù)數(shù),請稍后再試,/(ㄒoㄒ)/~~   id: " +id;
    }

上面 @HystrixCommand 注解里面的 commandProperties 配置的屬性的意思是:假設(shè)在 10s(時(shí)間窗口期) 的時(shí)間,10(請求次數(shù)) 次請求里面,有 (失敗率)次是失?。ㄕ{(diào)用了兜底的方法)的,那么我們的斷路器就會(huì)發(fā)生作用。

那么為什么要配置這些參數(shù)呢?首先看下官網(wǎng)下的這張圖片,一個(gè)斷路器的打開和關(guān)閉是按照以下圖片的這五個(gè)過程。

1、請求的次數(shù)是否達(dá)到了峰值的次數(shù)。

2、錯(cuò)誤次數(shù)的百分比是否達(dá)到了閾值。

3、斷路器的狀態(tài)將從關(guān)閉狀態(tài)轉(zhuǎn)換為開啟狀態(tài),開啟狀態(tài)就是跳閘了,用不了了。(正常情況為關(guān)閉狀態(tài))

4、當(dāng)斷路器處于開啟的狀態(tài)后,一段時(shí)間之內(nèi)所有的請求都將無法使用。

5、等到時(shí)間窗口期一過,那么下一個(gè)請求嘗試著讓它通過一下,這個(gè)就是所謂的半開狀態(tài),如果這個(gè)請求還是無法通過,就說明斷路器還是處于開啟狀態(tài),如果這個(gè)請求通過了,說明服務(wù)已經(jīng)恢復(fù)了,那么就將斷路器恢復(fù)成關(guān)閉狀態(tài),再回到第一步,循環(huán)反復(fù)。

修改 PaymentController 類,添加如下的代碼:

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

啟動(dòng) cloud-provider-hystrix-payment8001 模塊進(jìn)行自測,首先測試正常調(diào)用的情況 http://localhost:8001/payment/circuit/31,如下圖:

然后測試異常調(diào)用的情況 http://localhost:8001/payment/circuit/-31,如下圖:

接下來進(jìn)行重點(diǎn)測試,多次異常調(diào)用,然后點(diǎn)擊正常調(diào)用,會(huì)出現(xiàn)正常調(diào)用也出現(xiàn)異常,等到一段時(shí)間之后,又自己恢復(fù)了,如下圖:

4.7.3 熔斷原理

先看下大神的結(jié)論,如下圖:

1、斷路器一開始處于斷開狀態(tài),當(dāng)失敗次數(shù)增多時(shí),斷路器變成開啟狀態(tài)。

2、等到一段時(shí)間之后,斷路器處于半開狀態(tài),嘗試的去處理請求。

3、當(dāng)處理成功之后,斷路器再次變成斷開狀態(tài)。

4、當(dāng)處理失敗之后,斷路器再次變成開啟狀態(tài)

4.7.4 熔斷類型

由以上分析可以得出結(jié)論,熔斷類型分為三種:熔斷打開、熔斷關(guān)閉和熔斷半開。

1、熔斷打開:請求不再進(jìn)行調(diào)用當(dāng)前服務(wù),內(nèi)部設(shè)置時(shí)鐘一般為 MTTR(平均故障處理時(shí)間),當(dāng)打開時(shí)長達(dá)到所設(shè)時(shí)鐘則進(jìn)入半熔斷狀態(tài)。

2、熔斷關(guān)閉:熔斷關(guān)閉不會(huì)對服務(wù)進(jìn)行熔斷

3、熔斷半開:部分請求根據(jù)規(guī)則調(diào)用當(dāng)前服務(wù),如果請求成功且符合規(guī)則則認(rèn)為當(dāng)前服務(wù)恢復(fù)正常,關(guān)閉熔斷。

4.7.5 官網(wǎng)斷路器流程圖

官網(wǎng)的步驟就是我們上面分析的那張圖,如下:

4.7.6  斷路器何時(shí)起作用

斷路器在什么情況下開始起作用?涉及到斷路器的三個(gè)重要參數(shù):快照時(shí)間窗、請求總數(shù)閥值、錯(cuò)誤百分比閥值。

1、快照時(shí)間窗:斷路器確定是否打開需要統(tǒng)計(jì)一些請求和錯(cuò)誤數(shù)據(jù),而統(tǒng)計(jì)的時(shí)間范圍就是快照時(shí)間窗,默認(rèn)為最近的 10 秒。

2、請求總數(shù)閥值:在快照時(shí)間窗內(nèi),必須滿足請求總數(shù)閥值才有資格熔斷。默認(rèn)為 20,意味著在 10 秒內(nèi),如果該 hystrix 命令的調(diào)用次數(shù)不足 20 次,即使所有的請求都超時(shí)或其他原因失敗,斷路器都不會(huì)打開。

3、錯(cuò)誤百分比閥值:當(dāng)請求總數(shù)在快照時(shí)間窗內(nèi)超過了閥值,比如發(fā)生了 30 次調(diào)用,如果在這 30 次調(diào)用中,有 15 次發(fā)生了超時(shí)異常,也就是超過 50% 的錯(cuò)誤百分比,在默認(rèn)設(shè)定 50% 閥值情況下,這時(shí)候就會(huì)將斷路器打開。

4.7.7 斷路器開啟或者關(guān)閉的條件

1、當(dāng)滿足一定的閥值的時(shí)候(默認(rèn)10 秒內(nèi)超過 20 個(gè)請求次數(shù))

2、當(dāng)失敗率達(dá)到一定的時(shí)候(默認(rèn) 10 秒內(nèi)超過 50% 的請求失?。?/p>

3、到達(dá)以上閥值,斷路器將會(huì)開啟。

4、當(dāng)開啟的時(shí)候,所有請求都不會(huì)進(jìn)行轉(zhuǎn)發(fā)

5、一段時(shí)間之后(默認(rèn)是 5 秒),這個(gè)時(shí)候斷路器是半開狀態(tài),會(huì)讓其中一個(gè)請求進(jìn)行轉(zhuǎn)發(fā)。如果成功,斷路器會(huì)關(guān)閉,若失敗,繼續(xù)開啟。重復(fù) 和 5

4.7.8 斷路器開啟之后

再有請求調(diào)用的時(shí)候,將不會(huì)調(diào)用主邏輯,而是直接調(diào)用降級 fallback。通過斷路器,實(shí)現(xiàn)了自動(dòng)地發(fā)現(xiàn)錯(cuò)誤并將降級邏輯切換為主邏輯,減少響應(yīng)延遲的效果。

原來的主邏輯要如何恢復(fù)呢?對于這一問題,hystrix 也為我們實(shí)現(xiàn)了自動(dòng)恢復(fù)功能。當(dāng)斷路器打開,對主邏輯進(jìn)行熔斷之后,hystrix 會(huì)啟動(dòng)一個(gè)休眠時(shí)間窗,在這個(gè)時(shí)間窗內(nèi),降級邏輯是臨時(shí)的成為主邏輯,當(dāng)休眠時(shí)間窗到期,斷路器將進(jìn)入半開狀態(tài),釋放一次請求到原來的主邏輯上,如果此次請求正常返回,那么斷路器將繼續(xù)閉合,主邏輯恢復(fù),如果這次請求依然有問題,斷路器繼續(xù)進(jìn)入打開狀態(tài),休眠時(shí)間窗重新計(jì)時(shí)。

4.7.9 Properties 屬性總結(jié)

@HystrixCommand 注解的 commandProperties 可能用到的所有屬性如下所示:

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

        commandProperties = {
                // 設(shè)置隔離策略,THREAD 表示線程池 SEMAPHORE:信號(hào)池隔離
                @HystrixProperty(name = "execution.isolation.strategy", value = "THREAD"),
                // 當(dāng)隔離策略選擇信號(hào)池隔離的時(shí)候,用來設(shè)置信號(hào)池的大小(最大并發(fā)數(shù))
                @HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "10"),
                // 配置命令執(zhí)行的超時(shí)時(shí)間
                @HystrixProperty(name = "execution.isolation.thread.timeoutinMilliseconds", value = "10"),
                // 是否啟用超時(shí)時(shí)間
                @HystrixProperty(name = "execution.timeout.enabled", value = "true"),
                // 執(zhí)行超時(shí)的時(shí)候是否中斷
                @HystrixProperty(name = "execution.isolation.thread.interruptOnTimeout", value = "true"),
                // 執(zhí)行被取消的時(shí)候是否中斷
                @HystrixProperty(name = "execution.isolation.thread.interruptOnCancel", value = "true"),
                // 允許回調(diào)方法執(zhí)行的最大并發(fā)數(shù)
                @HystrixProperty(name = "fallback.isolation.semaphore.maxConcurrentRequests", value = "10"),
                // 服務(wù)降級是否啟用,是否執(zhí)行回調(diào)函數(shù)
                @HystrixProperty(name = "fallback.enabled", value = "true"),
                // 是否啟用斷路器
                @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
                // 該屬性用來設(shè)置在滾動(dòng)時(shí)間窗中,斷路器熔斷的最小請求數(shù)。例如,默認(rèn)該值為 20 的時(shí)候,
                // 如果滾動(dòng)時(shí)間窗(默認(rèn)10秒)內(nèi)僅收到了19個(gè)請求, 即使這19個(gè)請求都失敗了,斷路器也不會(huì)打開。
                @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),
                // 該屬性用來設(shè)置在滾動(dòng)時(shí)間窗中,表示在滾動(dòng)時(shí)間窗中,在請求數(shù)量超過
                // circuitBreaker.requestVolumeThreshold 的情況下,如果錯(cuò)誤請求數(shù)的百分比超過50,
                // 就把斷路器設(shè)置為 "打開" 狀態(tài),否則就設(shè)置為 "關(guān)閉" 狀態(tài)。
                @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
                // 該屬性用來設(shè)置當(dāng)斷路器打開之后的休眠時(shí)間窗。 休眠時(shí)間窗結(jié)束之后,
                // 會(huì)將斷路器置為 "半開" 狀態(tài),嘗試熔斷的請求命令,如果依然失敗就將斷路器繼續(xù)設(shè)置為 "打開" 狀態(tài),
                // 如果成功就設(shè)置為 "關(guān)閉" 狀態(tài)。
                @HystrixProperty(name = "circuitBreaker.sleepWindowinMilliseconds", value = "5000"),
                // 斷路器強(qiáng)制打開
                @HystrixProperty(name = "circuitBreaker.forceOpen", value = "false"),
                // 斷路器強(qiáng)制關(guān)閉
                @HystrixProperty(name = "circuitBreaker.forceClosed", value = "false"),
                // 滾動(dòng)時(shí)間窗設(shè)置,該時(shí)間用于斷路器判斷健康度時(shí)需要收集信息的持續(xù)時(shí)間
                @HystrixProperty(name = "metrics.rollingStats.timeinMilliseconds", value = "10000"),
                // 該屬性用來設(shè)置滾動(dòng)時(shí)間窗統(tǒng)計(jì)指標(biāo)信息時(shí)劃分"桶"的數(shù)量,斷路器在收集指標(biāo)信息的時(shí)候會(huì)根據(jù)
                // 設(shè)置的時(shí)間窗長度拆分成多個(gè) "桶" 來累計(jì)各度量值,每個(gè)"桶"記錄了一段時(shí)間內(nèi)的采集指標(biāo)。
                // 比如 10 秒內(nèi)拆分成 10 個(gè)"桶"收集這樣,所以 timeinMilliseconds 必須能被 numBuckets 整除。否則會(huì)拋異常
                @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "10"),
                // 該屬性用來設(shè)置對命令執(zhí)行的延遲是否使用百分位數(shù)來跟蹤和計(jì)算。如果設(shè)置為 false, 那么所有的概要統(tǒng)計(jì)都將返回 -1。
                @HystrixProperty(name = "metrics.rollingPercentile.enabled", value = "false"),
                // 該屬性用來設(shè)置百分位統(tǒng)計(jì)的滾動(dòng)窗口的持續(xù)時(shí)間,單位為毫秒。
                @HystrixProperty(name = "metrics.rollingPercentile.timeInMilliseconds", value = "60000"),
                // 該屬性用來設(shè)置百分位統(tǒng)計(jì)滾動(dòng)窗口中使用 “ 桶 ”的數(shù)量。
                @HystrixProperty(name = "metrics.rollingPercentile.numBuckets", value = "60000"),
                // 該屬性用來設(shè)置在執(zhí)行過程中每個(gè) “桶” 中保留的最大執(zhí)行次數(shù)。如果在滾動(dòng)時(shí)間窗內(nèi)發(fā)生超過該設(shè)定值的執(zhí)行次數(shù),
                // 就從最初的位置開始重寫。例如,將該值設(shè)置為100, 滾動(dòng)窗口為10秒,若在10秒內(nèi)一個(gè) “桶 ”中發(fā)生了500次執(zhí)行,
                // 那么該 “桶” 中只保留 最后的100次執(zhí)行的統(tǒng)計(jì)。另外,增加該值的大小將會(huì)增加內(nèi)存量的消耗,并增加排序百分位數(shù)所需的計(jì)算時(shí)間。
                @HystrixProperty(name = "metrics.rollingPercentile.bucketSize", value = "100"),
                // 該屬性用來設(shè)置采集影響斷路器狀態(tài)的健康快照(請求的成功、 錯(cuò)誤百分比)的間隔等待時(shí)間。
                @HystrixProperty(name = "metrics.healthSnapshot.intervalinMilliseconds", value = "500"),
                // 是否開啟請求緩存
                @HystrixProperty(name = "requestCache.enabled", value = "true"),
                // HystrixCommand的執(zhí)行和事件是否打印日志到 HystrixRequestLog 中
                @HystrixProperty(name = "requestLog.enabled", value = "true"),
        },
        threadPoolProperties = {
                // 該參數(shù)用來設(shè)置執(zhí)行命令線程池的核心線程數(shù),該值也就是命令執(zhí)行的最大并發(fā)量
                @HystrixProperty(name = "coreSize", value = "10"),
                // 該參數(shù)用來設(shè)置線程池的最大隊(duì)列大小。當(dāng)設(shè)置為 -1 時(shí),線程池將使用 SynchronousQueue 實(shí)現(xiàn)的隊(duì)列,
                // 否則將使用 LinkedBlockingQueue 實(shí)現(xiàn)的隊(duì)列。
                @HystrixProperty(name = "maxQueueSize", value = "-1"),
                // 該參數(shù)用來為隊(duì)列設(shè)置拒絕閾值。 通過該參數(shù), 即使隊(duì)列沒有達(dá)到最大值也能拒絕請求。
                // 該參數(shù)主要是對 LinkedBlockingQueue 隊(duì)列的補(bǔ)充,因?yàn)?LinkedBlockingQueue
                // 隊(duì)列不能動(dòng)態(tài)修改它的對象大小,而通過該屬性就可以調(diào)整拒絕請求的隊(duì)列大小了。
                @HystrixProperty(name = "queueSizeRejectionThreshold", value = "5"),
        }
)
public String strConsumer() {
    return "hello 2020";
}
public String str_fallbackMethod()
{
    return "*****fall back str_fallbackMethod";
}
 

4.8 服務(wù)限流

后面會(huì)詳細(xì)講解 alibaba 的 Sentinel ,這邊就不展開講解了。

五、Hystrix 工作流程

5.1 官網(wǎng)圖例

5.2 步驟說明

1、創(chuàng)建 HystrixCommand(用在依賴的服務(wù)返回單個(gè)操作結(jié)果的時(shí)候) 或 HystrixObserableCommand(用在依賴的服務(wù)返回多個(gè)操作結(jié)果的時(shí)候) 對象。

2、命令執(zhí)行。其中 HystrixComand 實(shí)現(xiàn)了下面前兩種執(zhí)行方式;而 HystrixObservableCommand 實(shí)現(xiàn)了后兩種執(zhí)行方式:execute():同步執(zhí)行,從依賴的服務(wù)返回一個(gè)單一的結(jié)果對象, 或是在發(fā)生錯(cuò)誤的時(shí)候拋出異常。queue():異步執(zhí)行, 直接返回 一個(gè)Future對象, 其中包含了服務(wù)執(zhí)行結(jié)束時(shí)要返回的單一結(jié)果對象。observe():返回 Observable 對象,它代表了操作的多個(gè)結(jié)果,它是一個(gè) Hot Obserable(不論 "事件源" 是否有 "訂閱者",都會(huì)在創(chuàng)建后對事件進(jìn)行發(fā)布,所以對于 Hot Observable 的每一個(gè) "訂閱者" 都有可能是從 "事件源" 的中途開始的,并可能只是看到了整個(gè)操作的局部過程)。toObservable(): 同樣會(huì)返回 Observable 對象,也代表了操作的多個(gè)結(jié)果,但它返回的是一個(gè) Cold Observable(沒有 "訂閱者" 的時(shí)候并不會(huì)發(fā)布事件,而是進(jìn)行等待,直到有 "訂閱者" 之后才發(fā)布事件,所以對于 Cold Observable 的訂閱者,它可以保證從一開始看到整個(gè)操作的全部過程)。

3、若當(dāng)前命令的請求緩存功能是被啟用的, 并且該命令緩存命中, 那么緩存的結(jié)果會(huì)立即以 Observable 對象的形式 返回。

4、檢查斷路器是否為打開狀態(tài)。如果斷路器是打開的,那么 Hystrix 不會(huì)執(zhí)行命令,而是轉(zhuǎn)接到 fallback 處理邏輯(第 8 步);如果斷路器是關(guān)閉的,檢查是否有可用資源來執(zhí)行命令(第 步)。

5、線程池/請求隊(duì)列/信號(hào)量是否占滿。如果命令依賴服務(wù)的專有線程池和請求隊(duì)列,或者信號(hào)量(不使用線程池的時(shí)候)已經(jīng)被占滿, 那么 Hystrix 也不會(huì)執(zhí)行命令, 而是轉(zhuǎn)接到 fallback 處理邏輯(第8步)。

6、Hystrix 會(huì)根據(jù)我們編寫的方法來決定采取什么樣的方式去請求依賴服務(wù)。HystrixCommand.run() :返回一個(gè)單一的結(jié)果,或者拋出異常。HystrixObservableCommand.construct(): 返回一個(gè)Observable 對象來發(fā)射多個(gè)結(jié)果,或通過 onError 發(fā)送錯(cuò)誤通知。

7、Hystrix 會(huì)將 "成功"、"失敗"、"拒絕"、"超時(shí)" 等信息報(bào)告給斷路器, 而斷路器會(huì)維護(hù)一組計(jì)數(shù)器來統(tǒng)計(jì)這些數(shù)據(jù)。斷路器會(huì)使用這些統(tǒng)計(jì)數(shù)據(jù)來決定是否要將斷路器打開,來對某個(gè)依賴服務(wù)的請求進(jìn)行 "熔斷/短路"。

8、當(dāng)命令執(zhí)行失敗的時(shí)候, Hystrix 會(huì)進(jìn)入 fallback 嘗試回退處理, 我們通常也稱該操作為 "服務(wù)降級"。而能夠引起服務(wù)降級處理的情況有下面幾種:第4步: 當(dāng)前命令處于"熔斷/短路"狀態(tài),斷路器是打開的時(shí)候。第 步: 當(dāng)前命令的線程池、 請求隊(duì)列或 者信號(hào)量被占滿的時(shí)候。第 步:HystrixObservableCommand.construct() 或 HystrixCommand.run() 拋出異常的時(shí)候。

9、當(dāng) Hystrix 命令執(zhí)行成功之后, 它會(huì)將處理結(jié)果直接返回或是以 Observable 的形式返回。

注意:

如果我們沒有為命令實(shí)現(xiàn)降級邏輯或者在降級處理邏輯中拋出了異常, Hystrix 依然會(huì)返回一個(gè) Observable 對象, 但是它不會(huì)發(fā)射任何結(jié)果數(shù)據(jù), 而是通過 onError 方法通知命令立即中斷請求,并通過 onError() 方法將引起命令失敗的異常發(fā)送給調(diào)用者。

六、服務(wù)監(jiān)控 hystrixDashboard

6.1 概述

除了隔離依賴服務(wù)的調(diào)用以外,Hystrix 還提供了準(zhǔn)實(shí)時(shí)的調(diào)用監(jiān)控(Hystrix Dashboard), Hystrix 會(huì)持續(xù)地記錄所有通過 Hystrix 發(fā)起的請求的執(zhí)行信息,并以統(tǒng)計(jì)報(bào)表和圖形的形式展示給用戶,包括每秒執(zhí)行多少請求多少成功,多少失敗等。Netflix 通過 hystrix-metrics-event-stream 項(xiàng)目實(shí)現(xiàn)了對以上指標(biāo)的監(jiān)控。Spring Cloud 也提供了 Hystrix Dashboard 的整合,對監(jiān)控內(nèi)容轉(zhuǎn)化成可視化界面。

6.2 搭建儀表盤

新建一個(gè) cloud-consumer-hystrix-dashboard9001 子模塊,pom.xml 內(nèi)容如下所示:

<?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">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.springcloud</groupId>
        <artifactId>SpringCloud</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

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

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <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>

application.yml 內(nèi)容如下所示:

server:
  port: 9001

啟動(dòng)類的代碼如下所示,注意這塊新加一個(gè) @EnableHystrixDashboard 注解

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

針對于所有的 Provider 微服務(wù)提供類(8001/8002/8003)都需要監(jiān)控依賴配置,即需要確保 pom.xml 中存在以下的注解:

<!-- actuator監(jiān)控信息完善 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

啟動(dòng) cloud-consumer-hystrix-dashboard9001 模塊,并輸入 http://localhost:9001/hystrix 進(jìn)行測試,如下圖:

6.3 服務(wù)監(jiān)控演示

修改 cloud-provider-hystrix-payment8001 的啟動(dòng)類,因?yàn)樾掳姹?nbsp;Hystrix 需要在主啟動(dòng)類 MainAppHystrix8001 中指定監(jiān)控路徑,代碼如下:

@SpringBootApplication
@EnableEurekaClient //本服務(wù)啟動(dòng)后會(huì)自動(dòng)注冊進(jìn)eureka服務(wù)中
@EnableCircuitBreaker
public class PaymentHystrixMain8001
{
    public static void main(String[] args)
    {
        SpringApplication.run(PaymentHystrixMain8001.class,args);
    }

    /**
     *此配置是為了服務(wù)監(jiān)控而配置,與服務(wù)容錯(cuò)本身無關(guān),springcloud升級后的坑
     *ServletRegistrationBean因?yàn)閟pringboot的默認(rèn)路徑不是"/hystrix.stream",
     *只要在自己的項(xiàng)目里配置上下面的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;
    }
}

啟動(dòng) cloud-eureka-server7001 和 cloud-provider-hystrix-payment8001 模塊,使用 9001 監(jiān)控 8001,如下圖:

正常調(diào)用的測試地址為:http://localhost:8001/payment/circuit/31,異常調(diào)用的測試地址為:http://localhost:8001/payment/circuit/-31,先訪問正確地址,再訪問異常地址,再正確地址,會(huì)發(fā)現(xiàn)圖示斷路器都是慢慢放開的。

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

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

整圖說明如下:

到此這篇關(guān)于SpringCloud Hystrix 斷路器的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)SpringCloud Hystrix 斷路器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • JAVA對象分析之偏向鎖、輕量級鎖、重量級鎖升級過程

    JAVA對象分析之偏向鎖、輕量級鎖、重量級鎖升級過程

    這篇文章主要介紹了JAVA對象分析之偏向鎖、輕量級鎖、重量級鎖升級過程,又對這方面感興趣的同學(xué)可以跟著一起研究下
    2021-02-02
  • SpringBoot優(yōu)化接口響應(yīng)時(shí)間的九個(gè)技巧

    SpringBoot優(yōu)化接口響應(yīng)時(shí)間的九個(gè)技巧

    在實(shí)際開發(fā)中,提升接口響應(yīng)速度是一件挺重要的事,特別是在面臨大量用戶請求的時(shí)候,本文為大家整理了9個(gè)SpringBoot優(yōu)化接口響應(yīng)時(shí)間的技巧,希望對大家有所幫助
    2024-01-01
  • 詳解java 中Spring jsonp 跨域請求的實(shí)例

    詳解java 中Spring jsonp 跨域請求的實(shí)例

    這篇文章主要介紹了詳解java 中Spring jsonp 跨域請求的實(shí)例的相關(guān)資料,jsonp 可用于解決主流瀏覽器的跨域數(shù)據(jù)訪問的問題,需要的朋友可以參考下
    2017-08-08
  • 通過Java實(shí)現(xiàn)bash命令過程解析

    通過Java實(shí)現(xiàn)bash命令過程解析

    這篇文章主要介紹了通過Java實(shí)現(xiàn)bash命令過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-01-01
  • Java基礎(chǔ)之不簡單的數(shù)組

    Java基礎(chǔ)之不簡單的數(shù)組

    數(shù)組(Array)是有序的元素序列。 若將有限個(gè)類型相同的變量的集合命名,那么這個(gè)名稱為數(shù)組名。組成數(shù)組的各個(gè)變量稱為數(shù)組的分量,也稱為數(shù)組的元素,有時(shí)也稱為下標(biāo)變量
    2021-09-09
  • 關(guān)于mybatis mapper類注入失敗的解決方案

    關(guān)于mybatis mapper類注入失敗的解決方案

    這篇文章主要介紹了關(guān)于mybatis mapper類注入失敗的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-04-04
  • SpringBoot如何解決跨域Cores問題

    SpringBoot如何解決跨域Cores問題

    這篇文章主要介紹了SpringBoot如何解決跨域Cores問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-09-09
  • Java基于IDEA實(shí)現(xiàn)http編程的示例代碼

    Java基于IDEA實(shí)現(xiàn)http編程的示例代碼

    這篇文章主要介紹了Java基于IDEA實(shí)現(xiàn)http編程的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • Java基礎(chǔ)篇之serialVersionUID用法及注意事項(xiàng)詳解

    Java基礎(chǔ)篇之serialVersionUID用法及注意事項(xiàng)詳解

    這篇文章主要給大家介紹了關(guān)于Java基礎(chǔ)篇之serialVersionUID用法及注意事項(xiàng)的相關(guān)資料,SerialVersionUID屬性是用于序列化/反序列化可序列化類的對象的標(biāo)識(shí)符,我們可以用它來記住可序列化類的版本,以驗(yàn)證加載的類和序列化對象是否兼容,需要的朋友可以參考下
    2024-02-02
  • 詳解Eclipse提交項(xiàng)目到GitHub以及解決代碼沖突

    詳解Eclipse提交項(xiàng)目到GitHub以及解決代碼沖突

    這篇文章主要介紹了詳解Eclipse提交項(xiàng)目到GitHub以及解決代碼沖突,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-03-03

最新評論