11-SpringCloud Hystrix

Hystrix簡介

分散式系統面臨的問題

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

服務雪崩

多個微服務之間調用的時候,假設微服務A調用微服務B和微服務C,微服務B和微服務C又調用其它的微服務,這就是所謂的「扇出」。如果扇出的鏈路上某個微服務的調用響應時間過長或者不可用,對微服務A的調用就會佔用越來越多的系統資源,進而引起系統崩潰,所謂的「雪崩效應」.對於高流量的應用來說,單一的後避依賴可能會導致所有伺服器上的所有資源都在幾秒鐘內飽和。比失敗更糟糕的是,這些應用程式還可能導致服務之間的延遲增加,備份隊列,執行緒和其他系統資源緊張,導致整個系統發生更多的級聯故障。這些都表示需要對故障和延遲進行隔離和管理,以便單個依賴關係的失敗,不能取消整個應用程式或系統。

 

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

概述

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

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

Hystrix停更進維

Hystrix的作用

  1. 服務降級
  2. 服務熔斷
  1. 接近實時的監控

官方資料

GitHub Wiki

停更進維通知

GitHub

  1. 被動修bugs
  2. 不再接受合併請求
  1. 不再發布新版本

Hystrix的服務降級熔斷限流概念

服務降級

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

那些情況會觸發降級

  1. 程式運行異常
  2. 調用超時
  1. 服務熔斷觸發服務降級
  2. 執行緒池/訊號量打滿也會導致服務降級

服務熔斷

類比保險絲達到最大服務訪問後,直接拒絕訪問,拉閘限電,然後調用服務降級的方法並返回友好提示。

服務的降級 -> 進而熔斷 -> 恢復調用鏈路

服務限流

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

限流手段

  1. 隨機
  2. Token過濾
  1. 計算過濾

提供者服務構建

創建提供者項目

搭建cloud-provider-hygtrix-payment8001工程

修改POM.xml

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

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

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </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>
        <!-- dependent on common modules -->
        <dependency>
            <groupId>com.dance</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</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>

新建yml配置

server:
  port: 8001

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

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

新建主啟動類

package com.dance.springcloud;

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

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

新建Service

package com.dance.springcloud.service;

import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 *
 */
@Service
public class PaymentService {
    
    
    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.MILLISECONDS.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "執行緒池:  " + Thread.currentThread().getName() + " id:  " + id + "\t" + "O(∩_∩)O哈哈~" + "  耗時(秒): 3";
    }
}

新建Controller

package com.dance.springcloud.controller;

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

import javax.annotation.Resource;

/**
 *
 */
@RestController
@Slf4j
public class PaymentController {
    @Resource
    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) {
        String result = paymentService.paymentInfo_TimeOut(id);
        log.info("*****result: " + result);
        return result;
    }
}

正常測試

  • 啟動Eureka集群
  • 啟動8001工程
  • 瀏覽器測試
  • paymentInfo_OK測試
  • paymentInfo_TimeOut測試

上述module均OK

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

JMeter高並發壓測

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

Jmeter概述

Jmeter官網

The Apache JMeter™ application is open source software, a 100% pure Java application designed to load test functional behavior and measure performance. It was originally designed for testing Web Applications but has since expanded to other test functions.

Jmeter下載

下載地址

//jmeter.apache.org/download_jmeter.cgi

點擊即可下載,下載完成後得到的是壓縮包,解壓就可以直接使用,不用安裝

Jmeter啟動

雙擊jmeter/bin/jmeter.bat即可

默認是英文的

切換中文語言

在Options中找到Choose Language找到Chinese(Simplified) 點擊 即可切換為中文簡體

Jmeter壓測測試

新建執行緒組

右擊Test Plan

設置執行緒屬性

200 X 100 = 20000次

添加Http請求

在執行緒組右擊

設置Http請求

啟動和停止

測試

啟動Jmeter後再次訪問//localhost:8001/payment/hystrix/ok/1介面,發現連正常的介面都被拖慢了

 

看演示結果:拖慢,原因:tomcat的默認的工作執行緒數被打滿了,沒有多餘的執行緒來分解壓力和處理。

Jmeter壓測結論

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

消費者服務構建

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

新建80工程

新建cloud-consumer-feign-hystrix-order80工程

修改POM.xml

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

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

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </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>
        <!-- dependent on common modules -->
        <dependency>
            <groupId>com.dance</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</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>
        <!--一般基礎通用配置-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

新建yml配置

server:
  port: 80

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

新建主啟動類

package com.dance.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
//@EnableHystrix
public class OrderHystrixMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderHystrixMain80.class, args);
    }
}

新建Service

package com.dance.springcloud.service;

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

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

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

新建Controller

package com.dance.springcloud.controller;

import com.dance.springcloud.service.PaymentHystrixService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@Slf4j
public class OrderHystirxController {
    @Resource
    private PaymentHystrixService paymentHystrixService;

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

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

正常測試

//localhost/consumer/payment/hystrix/ok/1

高並發測試

2W個執行緒壓8001

消費端80微服務再去訪問正常的Ok微服務8001地址

//localhost/consumer/payment/hystrix/ok/32

消費者80被拖慢

原因:8001同一層次的其它介面服務被困死,因為tomcat執行緒池裡面的工作執行緒已經被擠占完畢。

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

降級容錯解決的維度要求

時導致伺服器變慢(轉圈) – 超時不再等待

出錯(宕機或程式運行出錯) – 出錯要有兜底

解決:

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

提供者服務降級(fallback)

降級配置 – @HystrixCommand

8001先從自身找問題

設置自身調用超時時間的峰值,峰值內可以正常運行,超過了需要有兜底的方法處埋,作服務降級fallback。

8001fallback

業務類啟用 – @HystrixCommand報異常後如何處理

—旦調用服務方法失敗並拋出了錯誤資訊後,會自動調用@HystrixCommand標註好的fallbackMethod調用類中的指定方法

修改提供者Service

package com.dance.springcloud.service;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 *
 */
@Service
public class PaymentService {


    public String paymentInfo_OK(Integer id) {
        return "執行緒池:  " + Thread.currentThread().getName() + "  paymentInfo_OK,id:  " + id + "\t" + "O(∩_∩)O哈哈~";
    }

    /**
     * HystrixCommand : 指定需要降級的方法
     * fallbackMethod = "paymentInfo_TimeOutHandler" : 降級後調用的方法名稱
     * @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000") : 設置方法執行時間超過3s就超時
     */
    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler", commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
    })
    public String paymentInfo_TimeOut(Integer id) {
        try {
        int age = 10/0; TimeUnit.MILLISECONDS.sleep(5
000); } catch (InterruptedException e) { e.printStackTrace(); } return "執行緒池: " + Thread.currentThread().getName() + " id: " + id + "\t" + "O(∩_∩)O哈哈~" + " 耗時(秒): 3"; } //用來善後的方法 public String paymentInfo_TimeOutHandler(Integer id) { return "執行緒池: " + Thread.currentThread().getName() + " 8001系統繁忙或者運行報錯,請稍後再試,id: " + id + "\t" + "o(╥﹏╥)o"; } }

上面故意製造兩種異常:

  1. int age = 10/0,計算異常
  2. 我們能接受3秒鐘,它運行5秒鐘,超時異常。

當前服務不可用了,做服務降級,兜底的方案都是paymentInfo_TimeOutHandler

主啟動類激活

添加新註解@EnableCircuitBreaker

package com.dance.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 PaymentHystrixMain8001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentHystrixMain8001.class, args);
    }
}

記得是提供者主啟動類,我這裡就添加到調用者主啟動類上面去了,感覺咋都不好使【哭】

測試

  1. 重啟提供者服務
  2. 瀏覽器測試
  1. //localhost:8001/payment/hystrix/timeout/1

測試成功,應為超時後調用了fallbackMethod

消費者服務降級(fallback)

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

修改yml配置

#開啟
feign:
  hystrix:
    enabled: true

修改主啟動類

package com.dance.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
// 開啟斷路器
@EnableHystrix
public class OrderHystrixMain80 {
    public static void main(String[] args) {
        SpringApplication.run(OrderHystrixMain80.class, args);
    }
}

修改Controller

package com.dance.springcloud.controller;

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

import javax.annotation.Resource;

@RestController
@Slf4j
public class OrderHystirxController {
    @Resource
    private PaymentHystrixService paymentHystrixService;

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

    @HystrixCommand(fallbackMethod = "paymentTimeOutFallbackMethod", commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "1500")
    })
    @GetMapping("/consumer/payment/hystrix/timeout/{id}")
    public String paymentInfo_TimeOut(@PathVariable("id") Integer id) {
        return paymentHystrixService.paymentInfo_TimeOut(id);
    }
    //善後方法
    public String paymentTimeOutFallbackMethod(@PathVariable("id") Integer id){
        return "我是消費者80,對方支付系統繁忙請10秒鐘後再試或者自己運行出錯請檢查自己,o(╥﹏╥)o";
    }
}

測試

測試之前將提供者服務的參數 超時時間改為正常的

超時時間: 5s

方法阻塞時間: 3s

應為消費者設置的超時時間是1.5s

  1. 啟動Eureka集群
  2. 啟動消費者和提供者
  1. 測試
  1. //localhost/consumer/payment/hystrix/timeout/1

測試成功~超過1.5s調用自己的降級處理手段

消費者全局服務降級

每個業務方法對應一個兜底的方法,程式碼膨脹,太多了,這並不是我們需要的,我們這需要一些特定的方法有特定的處理就好了,其他的全部走默認就可以

解決方法

  1. 每個方法配置一個服務降級方法,技術上可以,但是不聰明
  2. N除了個別重要核心業務有專屬,其它普通的可以通過@DefaultProperties(defaultFallback = 「」)統一跳轉到統一處理結果頁面
  1. 通用的和獨享的各自分開,避免了程式碼膨脹,合理減少了程式碼量

修改Controller

package com.dance.springcloud.controller;

import com.dance.springcloud.service.PaymentHystrixService;
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 lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@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) {
        return paymentHystrixService.paymentInfo_OK(id);
    }

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

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

    // 下面是全局fallback方法
    public String payment_Global_FallbackMethod() {
        return "Global異常處理資訊,請稍後再試,/(ㄒoㄒ)/~~";
    }
}

測試

  • 重啟消費者服務
  • 測試

消費者統配服務降級

現在程式碼 降級方法和介面方法是放在一起的,程式碼混亂,需要分開

服務降級,客戶端去調用服務端,碰上服務端宕機或關閉

本次案例服務降級處理是在客戶端80實現完成的,與服務端8001沒有關係,只需要為Feign客戶端定義的介面添加一個服務降級處理的實現類即可實現解耦

未來我們要面對的異常

  • 運行
  • 超時
  • 宕機

新建Service實現類

package com.dance.springcloud.service.impl;

import com.dance.springcloud.service.PaymentHystrixService;
import org.springframework.stereotype.Component;

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

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

修改Service介面

package com.dance.springcloud.service;

import com.dance.springcloud.service.impl.PaymentFallbackService;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

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

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

測試

  1. 重啟消費者服務
  2. 正常訪問測試
  1. //localhost/consumer/payment/hystrix/ok/1
  2. 故意關閉微服務8001

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

Hystrix之服務熔斷理論

斷路器,相當於保險絲。

熔斷機制概述

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

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

Martin Fowler的相關論文

Hystrix之服務熔斷案例(上)

Hutool國產工具類

修改cloud-provider-hystrix-payment8001工程的Service

package com.dance.springcloud.service;

import cn.hutool.core.util.IdUtil;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.PathVariable;

import java.util.concurrent.TimeUnit;

/**
 *
 */
@Service
public class PaymentService {


    public String paymentInfo_OK(Integer id) {
        return "執行緒池:  " + Thread.currentThread().getName() + "  paymentInfo_OK,id:  " + id + "\t" + "O(∩_∩)O哈哈~";
    }

    /**
     * HystrixCommand : 指定需要降級的方法
     * fallbackMethod = "paymentInfo_TimeOutHandler" : 降級後調用的方法名稱
     * @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000") : 設置方法執行時間超過3s就超時
     */
    @HystrixCommand(fallbackMethod = "paymentInfo_TimeOutHandler", commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
    })
    public String paymentInfo_TimeOut(Integer id) {
        try {
            int age = 10/0;
            TimeUnit.MILLISECONDS.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "執行緒池:  " + Thread.currentThread().getName() + " id:  " + id + "\t" + "O(∩_∩)O哈哈~" + "  耗時(秒): 3";
    }

    //用來善後的方法
    public String paymentInfo_TimeOutHandler(Integer id) {
        return "執行緒池:  " + Thread.currentThread().getName() + "  8001系統繁忙或者運行報錯,請稍後再試,id:  " + id + "\t" + "o(╥﹏╥)o";
    }

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

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

}

HystrixCommandProperties配置類

package com.netflix.hystrix;

...

public abstract class HystrixCommandProperties {
    private static final Logger logger = LoggerFactory.getLogger(HystrixCommandProperties.class);

    /* defaults */
    /* package */ static final Integer default_metricsRollingStatisticalWindow = 10000;// default => statisticalWindow: 10000 = 10 seconds (and default of 10 buckets so each bucket is 1 second)
    private static final Integer default_metricsRollingStatisticalWindowBuckets = 10;// default => statisticalWindowBuckets: 10 = 10 buckets in a 10 second window so each bucket is 1 second
    private static final Integer default_circuitBreakerRequestVolumeThreshold = 20;// default => statisticalWindowVolumeThreshold: 20 requests in 10 seconds must occur before statistics matter
    private static final Integer default_circuitBreakerSleepWindowInMilliseconds = 5000;// default => sleepWindow: 5000 = 5 seconds that we will sleep before trying again after tripping the circuit
    private static final Integer default_circuitBreakerErrorThresholdPercentage = 50;// default => errorThresholdPercentage = 50 = if 50%+ of requests in 10 seconds are failures or latent then we will trip the circuit
    private static final Boolean default_circuitBreakerForceOpen = false;// default => forceCircuitOpen = false (we want to allow traffic)
    /* package */ static final Boolean default_circuitBreakerForceClosed = false;// default => ignoreErrors = false 
    private static final Integer default_executionTimeoutInMilliseconds = 1000; // default => executionTimeoutInMilliseconds: 1000 = 1 second
    private static final Boolean default_executionTimeoutEnabled = true;

    ...
}

Hystrix之服務熔斷案例(下)

修改cloud-provider-hystrix-payment8001工程的Controller

package com.dance.springcloud.controller;

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

import javax.annotation.Resource;

/**
 *
 */
@RestController
@Slf4j
public class PaymentController {
    @Resource
    private PaymentService paymentService;

    。。。。。。。//====服務熔斷
    @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

正確 – //localhost:8001/payment/circuit/1

錯誤 – //localhost:8001/payment/circuit/-1

測試流程

  1. 先通過正確地址訪問,確保介面正確
  2. 通過錯誤地址訪問,連續訪問六次,達到後面斷路器配置的閾值
  1. 再次通過正確地址訪問,發現還是錯誤返回,此時已經被熔斷,自動調用降級
  2. 然後通過正確地址多次訪問,達到指定閾值,熔斷恢復

多次錯誤,再來次正確,但錯誤得顯示

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

Hystrix之服務熔斷總結

大神結論

Martin Fowler的相關論文

熔斷類型

  • 熔斷打開:請求不再進行調用當前服務,內部設置時鐘一般為MTTR(平均故障處理時間),當打開時長達到所設時鐘則進入半熔斷狀態。
  • 熔斷關閉:熔斷關閉不會對服務進行熔斷。
  • 熔斷半開:部分請求根據規則調用當前服務,如果請求成功且符合規則則認為當前服務恢復正常,關閉熔斷。

官網斷路器流程圖

 

 

官網步驟

The precise way that the circuit opening and closing occurs is as follows:

Assuming the volume across a circuit meets a certain threshold : HystrixCommandProperties.circuitBreakerRequestVolumeThreshold()

And assuming that the error percentage, as defined above exceeds the error percentage defined in : HystrixCommandProperties.circuitBreakerErrorThresholdPercentage()

Then the circuit-breaker transitions from CLOSED to OPEN.

While it is open, it short-circuits all requests made against that circuit-breaker.

After some amount of time (HystrixCommandProperties.circuitBreakerSleepWindowInMilliseconds()), the next request is let through. If it fails, the command stays OPEN for the sleep window. If it succeeds, it transitions to CLOSED and the logic in 1) takes over again.

link

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

//=====服務熔斷
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
    @HystrixProperty(name = "circuitBreaker.enabled",value = "true"),// 是否開啟斷路器
    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold",value = "10"),// 請求次數
    @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds",value = "10000"), // 時間窗口期
    @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage",value = "60"),// 失敗率達到多少後跳閘
})
public String paymentCircuitBreaker(@PathVariable("id") Integer id) {
    ...
}

涉及到斷路器的三個重要參數:

快照時間窗:斷路器確定是否打開需要統計一些請求和錯誤數據,而統計的時間範圍就是快照時間窗,默認為最近的10秒。

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

錯誤百分比閥值:當請求總數在快照時間窗內超過了閥值,比如發生了30次調用,如果在這30次調用中,有15次發生了超時異常,也就是超過50%的錯誤百分比,在默認設定50%閥值情況下,這時候就會將斷路器打開。

斷路器開啟或者關閉的條件

  • 到達以下閥值,斷路器將會開啟:
  • 當滿足一定的閥值的時候(默認10秒內超過20個請求次數)
  • 當失敗率達到一定的時候(默認10秒內超過50%的請求失敗)
  • 當開啟的時候,所有請求都不會進行轉發
  • 一段時間之後(默認是5秒),這個時候斷路器是半開狀態,會讓其中一個請求進行轉發。如果成功,斷路器會關閉,若失敗,繼續開啟。

斷路器打開之後

1:再有請求調用的時候,將不會調用主邏輯,而是直接調用降級fallback。通過斷路器,實現了自動地發現錯誤並將降級邏輯切換為主邏輯,減少響應延遲的效果。

2:原來的主邏輯要如何恢復呢?

對於這一問題,hystrix也為我們實現了自動恢復功能。

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

All配置

@HystrixCommand(fallbackMethod = "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 doSomething() {
    ...
}

Hystrix工作流程最後總結

服務限流 – 後面高級篇講解alibaba的Sentinel說明

官方解釋

官網圖例

步驟說明

  • 創建HystrixCommand (用在依賴的服務返回單個操作結果的時候)或HystrixObserableCommand(用在依賴的服務返回多個操作結果的時候)對象。
  • 命令執行。
  • 其中 HystrixCommand實現了下面前兩種執行方式
  • execute():同步執行,從依賴的服務返回一個單一的結果對象或是在發生錯誤的時候拋出異常。
  • queue():非同步執行,直接返回一個Future對象,其中包含了服務執行結束時要返回的單一結果對象。
  • 而 HystrixObservableCommand實現了後兩種執行方式:
  • obseve():返回Observable對象,它代表了操作的多個統果,它是一個Hot Observable (不論「事件源」是否有「訂閱者」,都會在創建後對事件進行發布,所以對於Hot Observable的每一個「訂閱者」都有可能是從「事件源」的中途開始的,並可能只是看到了整個操作的局部過程)。
  • toObservable():同樣會返回Observable對象,也代表了操作的多個結果,但它返回的是一個Cold Observable(沒有「訂間者」的時候並不會發布事件,而是進行等待,直到有「訂閱者”之後才發布事件,所以對於Cold Observable 的訂閱者,它可以保證從一開始看到整個操作的全部過程)。
  • 若當前命令的請求快取功能是被啟用的,並且該命令快取命中,那麼快取的結果會立即以Observable對象的形式返回。
  • 檢查斷路器是否為打開狀態。如果斷路器是打開的,那麼Hystrix不會執行命令,而是轉接到fallback處理邏輯(第8步);如果斷路器是關閉的,檢查是否有可用資源來執行命令(第5步)。
  • 執行緒池/請求隊列訊號量是否佔滿。如果命令依賴服務的專有執行緒地和請求隊列,或者訊號量(不使用執行緒的時候)已經被佔滿,那麼Hystrix也不會執行命令,而是轉接到fallback處理理輯(第8步) 。
  • Hystrix會根據我們編寫的方法來決定採取什麼樣的方式去請求依賴服務。
  • HystrixCommand.run():返回一個單一的結果,或者拋出異常。
  • HystrixObservableCommand.construct():返回一個Observable對象來發射多個結果,或通過onError發送錯誤通知。
  • Hystix會將「成功」、「失敗」、「拒絕」、「超時」 等資訊報告給斷路器,而斷路器會維護一組計數器來統計這些數據。斷路器會使用這些統計數據來決定是否要將斷路器打開,來對某個依賴服務的請求進行”熔斷/短路”。
  • 當命令執行失敗的時候,Hystix會進入fallback嘗試回退處理,我們通常也稱波操作為「服務降級」。而能夠引起服務降級處理的情況有下面幾種:
  • 第4步∶當前命令處於「熔斷/短路」狀態,斷洛器是打開的時候。
  • 第5步∶當前命令的錢程池、請求隊列或者訊號量被佔滿的時候。
  • 第6步∶HystrixObsevableCommand.construct()或HytrixCommand.run()拋出異常的時候。
  • 當Hystrix命令執行成功之後,它會將處理結果直接返回或是以Observable的形式返回。

tips:如果我們沒有為命令實現降級邏輯或者在降級處理邏輯中拋出了異常,Hystrix依然會運回一個Obsevable對象,但是它不會發射任結果數慣,而是通過onError方法通知命令立即中斷請求,並通過onError方法將引起命令失敗的異常發送給調用者。

Hystrix圖形化Dashboard搭建

概述

除了隔離依賴服務的調用以外,Hystrix還提供了准實時的調用監控(Hystrix Dashboard),Hystrix會持續地記錄所有通過Hystrix發起的請求的執行資訊,並以統計報表和圖形的形式展示給用戶,包括每秒執行多少請求多少成功,多少失敗等。

Netflix通過hystrix-metrics-event-stream項目實現了對以上指標的監控。Spring Cloud也提供了Hystrix Dashboard的整合,對監控內容轉化成可視化介面。

新建儀錶盤9001工程

新建cloud-consumer-hystrix-dashboard9001工程

修改POM.xml

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

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

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

新建yml配置

server: 
    port: 9001

新建主啟動類

package com.dance.springcloud;

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

@SpringBootApplication
// 開啟Hystrix儀錶盤
@EnableHystrixDashboard
public class HystrixDashboardMain9001 {
    public static void main(String[] args) {
        SpringApplication.run(HystrixDashboardMain9001.class, args);
    }
}

所有提供者添加監控依賴

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

測試

  1. 啟動cloud-consumer-hystrix-dashboard9001該微服務後續將監控微服務8001
  2. 瀏覽器輸入//localhost:9001/hystrix

 

Hystrix圖形化Dashboard監控實戰

修改cloud-provider-hystrix-payment8001

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

修改POM.xml

查看是否依賴監控,如果沒有就添加一下

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

修改主啟動類

package com.dance.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.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;

/**
 *
 */
@SpringBootApplication
@EnableEurekaClient
// 開啟降級激活
@EnableCircuitBreaker
public class PaymentHystrixMain8001 {
    public static void main(String[] args) {
        SpringApplication.run(PaymentHystrixMain8001.class, args);
    }
    /**
     *此配置是為了服務監控而配置,與服務容錯本身無關,springcloud升級後的坑
     *ServletRegistrationBean因為springboot的默認路徑不是"/hystrix.stream",
     *只要在自己的項目里配置上下面的servlet就可以了
     *否則,Unable to connect to Command Metric Stream 404
     */
    @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;
    }
}

測試

  1. 重啟8001
  2. 訪問
  1. //localhost:9001/hystrix
  1. 輸入框輸入://localhost:8001/hystrix.stream

  1. 通過瀏覽器訪問介面
  1. 正確: //localhost:8001/payment/circuit/1
  2. 錯誤: //localhost:8001/payment/circuit/-1
  1. 點擊按鈕查看
  1. 這裡有一個問題,如果沒有訪問的話,展示的是一個Loading….沒有報錯

觀察監控窗口

9001監控8001 – 填寫監控地址 – //localhost:8001/hystrix.stream//localhost:9001/hystrix頁面的輸入框。

  • 測試地址

測試通過

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

 

 

如何看?

  • 7色

  • 1圈:
  • 實心圓:共有兩種含義。它通過顏色的變化代表了實例的健康程度,它的健康度從綠色<黃色<橙色<紅色遞減。該實心圓除了顏色的變化之外,它的大小也會根據實例的請求流量發生變化,流量越大該實心圓就越大。所以通過該實心圓的展示,就可以在大量的實例中快速的發現故障實例和高壓力實例。
  • 1線
  • 曲線:用來記錄2分鐘內流量的相對變化,可以通過它來觀察到流量的上升和下降趨勢。

 

整圖說明

整圖說明2

這個東西看著挺多的,但是其實沒多少~, 其實如果有喜歡前端的小夥伴,學的還可以的話,可以扒一扒這個UI監控調用的介面,然後自己實現一套漂亮的監控介面

作者:彼岸舞

時間:2021\08\23

內容關於:Spring Cloud H版

本文屬於作者原創,未經允許,禁止轉發