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

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

 更新時間:2025年03月11日 14:28:22   作者:快樂的小三菊  
本文主要介紹了SpringCloud?Hystrix?斷路器的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(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ù)十個依賴關(guān)系,每個依賴關(guān)系在某些時候?qū)⒉豢杀苊獾厥 ?/p>

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

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

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

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

2.2 Hystrix 是什么

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

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

2.3 Hystrix 用途

1、服務(wù)降級

2、服務(wù)熔斷

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

2.4 現(xiàn)狀

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

三、Hystrix 重要概念

3.1 服務(wù)降級

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

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

3.2 服務(wù)熔斷

和家里以前用的保險絲類似,當(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ì),一秒鐘 個,有序進(jìn)行。

四、Hystrix 案例

4.1 構(gòu)建

創(chuàng)建一個子模塊工程 cloud-provider-hystrix-payment8001,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-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)行配置,只配置一臺服務(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

主啟動類的代碼如下所示:

package com.springcloud;

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

@SpringBootApplication
@EnableEurekaClient //本服務(wù)啟動后會自動注冊進(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";
    }

    /**
     * 超時訪問,演示降級
     */
    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;
    }
}

分別啟動 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,效果如下:

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

4.2 高并發(fā)測試

4.2.1 模塊自測

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

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

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

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

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

4.2.2 模塊公測

新建一個訂單模塊 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/

主啟動類的代碼如下所示:

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

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

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

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

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

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

4.4 上述結(jié)論

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

4.5 解決方案

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

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

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

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

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

4.6 服務(wù)降級

首先從 cloud-provider-hystrix-payment8001 模塊自身找問題,設(shè)置自身調(diào)用超時時間的峰值,峰值內(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";
    }

    /**
     * 超時訪問,演示降級
     */
    @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)用支付接口超時或異常:\t"+ "\t當(dāng)前線程池名字" + Thread.currentThread().getName();
    }
}

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

在主啟動類上添加 @EnableCircuitBreaker 注解,如下圖:

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

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

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

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

    /**
     * 超時訪問,演示降級
     */
    @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)用支付接口超時或異常:\t"+ "\t當(dāng)前線程池名字" + Thread.currentThread().getName();
    }
}

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

總結(jié):當(dāng)前服務(wù)超時或內(nèi)部錯誤時,都做服務(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

在主啟動類上添加 @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)行出錯請檢查自己,o(╥﹏╥)o";
    }
}

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

4.6.3 目前問題

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

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

4.6.4 配置全局方法

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

此種方法可以解決出現(xiàn)運(yùn)行時異常和超時異常的服務(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)行出錯請檢查自己,o(╥﹏╥)o";
    }
    public String payment_Global_FallbackMethod()
    {
        return "Global異常處理信息,請稍后再試,/(ㄒoㄒ)/~~";
    }
}

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

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

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

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

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

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

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

修改 cloud-consumer-feign-hystrix-order80 模塊,根據(jù) cloud-consumer-feign-hystrix-order80 已經(jīng)有的 PaymentHystrixService 接口,重新新建一個類 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);

}

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

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

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

4.7 服務(wù)熔斷

4.7.1 熔斷機(jī)制概述

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

在 SpringCloud 框架里,熔斷機(jī)制通過 Hystrix 實(shí)現(xiàn)。Hystrix 會監(jiān)控微服務(wù)間調(diào)用的狀況,當(dāng)失敗的調(diào)用到一定閾值,缺省是 5 秒內(nèi) 20 次調(diào)用失敗,就會啟動熔斷機(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"),// 時間窗口期
            @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)用成功,流水號: " + serialNumber;
    }
    public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id)
    {
        return "id 不能負(fù)數(shù),請稍后再試,/(ㄒoㄒ)/~~   id: " +id;
    }

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

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

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

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

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

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

5、等到時間窗口期一過,那么下一個請求嘗試著讓它通過一下,這個就是所謂的半開狀態(tài),如果這個請求還是無法通過,就說明斷路器還是處于開啟狀態(tài),如果這個請求通過了,說明服務(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;
    }

啟動 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)用,會出現(xiàn)正常調(diào)用也出現(xiàn)異常,等到一段時間之后,又自己恢復(fù)了,如下圖:

4.7.3 熔斷原理

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

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

2、等到一段時間之后,斷路器處于半開狀態(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è)置時鐘一般為 MTTR(平均故障處理時間),當(dāng)打開時長達(dá)到所設(shè)時鐘則進(jìn)入半熔斷狀態(tài)。

2、熔斷關(guān)閉:熔斷關(guān)閉不會對服務(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ù):快照時間窗、請求總數(shù)閥值、錯誤百分比閥值。

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

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

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

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

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

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

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

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

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

4.7.8 斷路器開啟之后

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

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

4.7.9 Properties 屬性總結(jié)

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

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

        commandProperties = {
                // 設(shè)置隔離策略,THREAD 表示線程池 SEMAPHORE:信號池隔離
                @HystrixProperty(name = "execution.isolation.strategy", value = "THREAD"),
                // 當(dāng)隔離策略選擇信號池隔離的時候,用來設(shè)置信號池的大?。ㄗ畲蟛l(fā)數(shù))
                @HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "10"),
                // 配置命令執(zhí)行的超時時間
                @HystrixProperty(name = "execution.isolation.thread.timeoutinMilliseconds", value = "10"),
                // 是否啟用超時時間
                @HystrixProperty(name = "execution.timeout.enabled", value = "true"),
                // 執(zhí)行超時的時候是否中斷
                @HystrixProperty(name = "execution.isolation.thread.interruptOnTimeout", value = "true"),
                // 執(zhí)行被取消的時候是否中斷
                @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è)置在滾動時間窗中,斷路器熔斷的最小請求數(shù)。例如,默認(rèn)該值為 20 的時候,
                // 如果滾動時間窗(默認(rèn)10秒)內(nèi)僅收到了19個請求, 即使這19個請求都失敗了,斷路器也不會打開。
                @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),
                // 該屬性用來設(shè)置在滾動時間窗中,表示在滾動時間窗中,在請求數(shù)量超過
                // circuitBreaker.requestVolumeThreshold 的情況下,如果錯誤請求數(shù)的百分比超過50,
                // 就把斷路器設(shè)置為 "打開" 狀態(tài),否則就設(shè)置為 "關(guān)閉" 狀態(tài)。
                @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
                // 該屬性用來設(shè)置當(dāng)斷路器打開之后的休眠時間窗。 休眠時間窗結(jié)束之后,
                // 會將斷路器置為 "半開" 狀態(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"),
                // 滾動時間窗設(shè)置,該時間用于斷路器判斷健康度時需要收集信息的持續(xù)時間
                @HystrixProperty(name = "metrics.rollingStats.timeinMilliseconds", value = "10000"),
                // 該屬性用來設(shè)置滾動時間窗統(tǒng)計指標(biāo)信息時劃分"桶"的數(shù)量,斷路器在收集指標(biāo)信息的時候會根據(jù)
                // 設(shè)置的時間窗長度拆分成多個 "桶" 來累計各度量值,每個"桶"記錄了一段時間內(nèi)的采集指標(biāo)。
                // 比如 10 秒內(nèi)拆分成 10 個"桶"收集這樣,所以 timeinMilliseconds 必須能被 numBuckets 整除。否則會拋異常
                @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "10"),
                // 該屬性用來設(shè)置對命令執(zhí)行的延遲是否使用百分位數(shù)來跟蹤和計算。如果設(shè)置為 false, 那么所有的概要統(tǒng)計都將返回 -1。
                @HystrixProperty(name = "metrics.rollingPercentile.enabled", value = "false"),
                // 該屬性用來設(shè)置百分位統(tǒng)計的滾動窗口的持續(xù)時間,單位為毫秒。
                @HystrixProperty(name = "metrics.rollingPercentile.timeInMilliseconds", value = "60000"),
                // 該屬性用來設(shè)置百分位統(tǒng)計滾動窗口中使用 “ 桶 ”的數(shù)量。
                @HystrixProperty(name = "metrics.rollingPercentile.numBuckets", value = "60000"),
                // 該屬性用來設(shè)置在執(zhí)行過程中每個 “桶” 中保留的最大執(zhí)行次數(shù)。如果在滾動時間窗內(nèi)發(fā)生超過該設(shè)定值的執(zhí)行次數(shù),
                // 就從最初的位置開始重寫。例如,將該值設(shè)置為100, 滾動窗口為10秒,若在10秒內(nèi)一個 “桶 ”中發(fā)生了500次執(zhí)行,
                // 那么該 “桶” 中只保留 最后的100次執(zhí)行的統(tǒng)計。另外,增加該值的大小將會增加內(nèi)存量的消耗,并增加排序百分位數(shù)所需的計算時間。
                @HystrixProperty(name = "metrics.rollingPercentile.bucketSize", value = "100"),
                // 該屬性用來設(shè)置采集影響斷路器狀態(tài)的健康快照(請求的成功、 錯誤百分比)的間隔等待時間。
                @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 時,線程池將使用 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ì)列不能動態(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ù)限流

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

五、Hystrix 工作流程

5.1 官網(wǎng)圖例

5.2 步驟說明

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

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

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

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

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

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

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

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

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

注意:

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

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

6.1 概述

除了隔離依賴服務(wù)的調(diào)用以外,Hystrix 還提供了準(zhǔn)實(shí)時的調(diào)用監(jiān)控(Hystrix Dashboard), Hystrix 會持續(xù)地記錄所有通過 Hystrix 發(fā)起的請求的執(zhí)行信息,并以統(tǒng)計報表和圖形的形式展示給用戶,包括每秒執(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 搭建儀表盤

新建一個 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

啟動類的代碼如下所示,注意這塊新加一個 @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>

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

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

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

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

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

啟動 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,先訪問正確地址,再訪問異常地址,再正確地址,會發(fā)現(xiàn)圖示斷路器都是慢慢放開的。

實(shí)心圓:共有兩種含義。它通過顏色的變化代表了實(shí)例的健康程度,它的健康度從綠色 < 黃色 < 橙色 < 紅色遞減。該實(shí)心圓除了顏色的變化之外,它的大小也會根據(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)時間的九個技巧

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

    在實(shí)際開發(fā)中,提升接口響應(yīng)速度是一件挺重要的事,特別是在面臨大量用戶請求的時候,本文為大家整理了9個SpringBoot優(yōu)化接口響應(yīng)時間的技巧,希望對大家有所幫助
    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í)價值,需要的朋友可以參考下
    2020-01-01
  • Java基礎(chǔ)之不簡單的數(shù)組

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

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

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

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

    SpringBoot如何解決跨域Cores問題

    這篇文章主要介紹了SpringBoot如何解決跨域Cores問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    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í)價值,需要的朋友們下面隨著小編來一起學(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)識符,我們可以用它來記住可序列化類的版本,以驗(yàn)證加載的類和序列化對象是否兼容,需要的朋友可以參考下
    2024-02-02
  • 詳解Eclipse提交項(xiàng)目到GitHub以及解決代碼沖突

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

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

最新評論