1. 程式人生 > 實用技巧 >服務熔斷Hystrix的替換方案Sentinel

服務熔斷Hystrix的替換方案Sentinel

1 服務熔斷Hystrix的替換方案

1.1 概述

  • 2018年底Netflix公司宣佈Hystrix已經足夠穩定,不再積極開發Hystrix,該專案處於維護模式。就目前來看Hystrix是比較穩定的,並且Hystrix只是停止開發新的版本,並不是完全停止維護,Bug什麼的依然會維護。因此,短期內,Hystrix依然是能繼續使用的。但是從長遠看,Hystrix總會達到它的生命週期,那麼Spring Cloud生態中是否有替代產品呢?

1.2 替換方案介紹

1.2.1 Alibaba Sentinel

  • Sentinel是阿里開源的一款熔斷器的實現,目前在Spring Cloud的孵化器專案Spring Cloud Alibaba中的一員Sentinel本身在阿里內部已經被大規模採用,非常穩定。因此,可以作為一個很好的替代品。

1.2.2 Resilience4J

  • Resilience4J是一款輕量、簡單,並且文件非常清晰、豐富的熔斷工具,這也是Hystri官方推薦的替代產品。不僅如此,Resilience4J還原生支援SpringBoot 1.x/2.x,而且監控也不像Hystrix一樣弄Dashboard/Hystrix等一堆輪子,而是支援和micrometer、prometheus以及Dropwizard metrics進行整合。

2 Sentinel概述

2.1 Sentinel官網

2.2 Sentinel是什麼?

  • 隨著微服務的流行,服務和服務之間的穩定性變得越來越重要。Sentinel 以流量為切入點,從流量控制、熔斷降級、系統負載保護等多個維度保護服務的穩定性。

  • Sentinel 具有以下特徵:

    • 豐富的應用場景:Sentinel 承接了阿里巴巴近 10 年的雙十一大促流量的核心場景,例如秒殺(即突發流量控制在系統容量可以承受的範圍)、訊息削峰填谷、叢集流量控制、實時熔斷下游不可用應用等。
    • 完備的實時監控:Sentinel 同時提供實時的監控功能。您可以在控制檯中看到接入應用的單臺機器秒級資料,甚至 500 臺以下規模的叢集的彙總執行情況。
    • 廣泛的開源生態:Sentinel 提供開箱即用的與其它開源框架/庫的整合模組,例如與 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相應的依賴並進行簡單的配置即可快速地接入 Sentinel。
    • 完善的 SPI 擴充套件點
      :Sentinel 提供簡單易用、完善的 SPI 擴充套件介面。您可以通過實現擴充套件介面來快速地定製邏輯。例如定製規則管理、適配動態資料來源等。

2.3 去哪裡下

2.4 能幹嘛?

3 安裝和執行Sentinel Dashboard

3.1 Sentinel的組成

  • Sentinel 的組成可以分為兩個部分:
  • 1️⃣核心庫(Java 客戶端):不依賴任何框架/庫,能夠運行於 Java 7 及以上的版本的執行時環境,同時對 Dubbo / Spring Cloud 等框架也有較好的支援(見 主流框架適配)。
  • 2️⃣控制檯(Dashboard):控制檯主要負責管理推送規則、監控、叢集限流分配管理、機器發現等。

3.2 安裝和執行步驟

# -Dserver.port=8080用於指定Sentinel控制檯埠為8080
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.0.jar
  • Sentinel的登入介面(訪問地址預設是http://localhost:8080/,使用者名稱和密碼為sentinel/sentinel):

4 初始化演示工程

4.1 啟動Nacos服務註冊中心和配置中心

4.2 新建Module

4.2.1 匯入相關依賴jar包的Maven座標

  • 修改部分:
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
  • 完整部分:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring_cloud_demo</artifactId>
        <groupId>org.sunxiaping</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud_alibaba_sentinel_service8401</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>

</project>

4.2.2 配置application.yml

server:
  port: 8401

spring:
  application:
    name: cloud-alibaba-sentinel-service
  cloud:
    nacos:
      discovery:
        # Nacos服務註冊中心地址
        server-addr: 127.0.0.1:8848
    sentinel:
      transport:
        # 配置Sentinel Dashboard地址
        dashboard: 127.0.0.1:8080
        # 預設8719埠,假設被佔用會自動從8719開始依次加1掃描,直到找到沒有被佔用的埠
        port: 8719
      # 取消懶載入
      eager: true

management:
  endpoints:
    web:
      exposure:
        include: '*'

4.2.3 配置啟動類

package com.sunxiaping.sentinel;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-15 10:02
 */
@EnableDiscoveryClient
@SpringBootApplication
public class Service8401Application {
    public static void main(String[] args) {
        SpringApplication.run(Service8401Application.class, args);
    }
}

4.2.4 編寫業務邏輯

  • FlowLimitController.java
package com.sunxiaping.sentinel.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-15 10:05
 */
@RestController
public class FlowLimitController {

    @GetMapping(value = "/testA")
    public String testA() {
        return "-------testA--------";
    }

    @GetMapping(value = "/testB")
    public String testB() {
        return "~~~~~~~~testB~~~~~~~~";
    }

}

5 流控規則

5.1 基本介紹

  • 資源名:唯一名稱,預設請求路徑。
  • 針對來源:Sentinel可以針對呼叫者進行限流,填寫微服務名,預設default(不區分來源)。
  • 閾值型別/單機閾值:
    • QPS(每秒請求數量):當呼叫該API的QPS達到閾值的時候,進行限流。
    • 執行緒數:當呼叫該API的執行緒數達到閾值的時候,進行限流。
  • 是否叢集:不需要叢集。
  • 流控模式:
    • 直接:API達到限流條件時,直接限流。
    • 關聯:當關聯的資源達到閾值的時候,就限流自己。
    • 鏈路:只記錄指定鏈路上的流量(指定資源從入口資源進來的流量,如果達到閾值,就進行限流)[API級別的針對來源]。
  • 流控效果:
    • 快速失敗:直接失敗,拋異常。
    • Warm Up:根據codeFactor(冷載入因子,預設為3)的值,從閾值/codeFactor,經過預熱時長,才達到設定的QPS閾值。
    • 排隊等待:勻速排隊,讓請求以勻速的速度通過,閾值型別必須設定為QPS,否則無效。

5.2 流控模式之直接(預設直接–>快速失敗)

5.2.1 配置說明

  • 表示1秒內查詢1次就是OK,如果超過1次,就直接–>快速失敗,報預設錯誤。

5.2.2 測試

  • 快速點選http://localhost:8401/testA。
  • 結果顯示:

5.3 流控模式之關聯

5.3.1 是什麼?

  • 當和A關聯的資源B達到閾值的時候,就限流A自己。
  • 比如:電商系統中,下訂單後面的流程就是支付,一旦支付超過閾值,就需要限制下訂單。

5.3.2 配置說明

  • 當關聯資源/testB的QPS的閾值超過1的時候,就限流A的訪問。

5.3.3 測試

  • 通過Postman併發測試http://localhost:8401/testB,然後通過瀏覽器訪問http://localhost:8401/testA。

5.4 流控模式之鏈路

5.4.1 配置說明

  • 多個請求呼叫http://localhost:8401/testA,如果1秒內請求次數超過1次,就會觸發限流。

5.5 流控效果之預熱

5.5.1 概述

  • Warm Up(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)方式,即預熱/冷啟動方式。當系統長期處於低水位的情況下,當流量突然增加時,直接把系統拉昇到高水位可能瞬間把系統壓垮。通過"冷啟動",讓通過的流量緩慢增加,在一定時間內逐漸增加到閾值上限,給冷系統一個預熱的時間,避免冷系統被壓垮。詳細文件可以參考 流量控制 - Warm Up 文件,具體的例子可以參見 WarmUpFlowDemo

  • 公式:閾值除以coldFactor(冷載入因子,預設為3),經過預熱後才會達到閾值。

5.5.2 配置說明

  • 系統初始化的的閾值是10/3約等於3,即閾值開始為3;經過5秒後閾值才慢慢升高恢復到10。

5.6 流控效果之排隊等待

5.6.1 概述

5.6.2 配置說明

6 降級規則

6.1 基本介紹

  • 除了流量控制以外,對呼叫鏈路中不穩定的資源進行熔斷降級也是保障高可用的重要措施之一。一個服務常常會呼叫別的模組,可能是另外的一個遠端服務、資料庫,或者第三方 API 等。例如,支付的時候,可能需要遠端呼叫銀聯提供的 API;查詢某個商品的價格,可能需要進行資料庫查詢。然而,這個被依賴服務的穩定性是不能保證的。如果依賴的服務出現了不穩定的情況,請求的響應時間變長,那麼呼叫服務的方法的響應時間也會變長,執行緒會產生堆積,最終可能耗盡業務自身的執行緒池,服務本身也變得不可用。

  • 現代微服務架構都是分散式的,由非常多的服務組成。不同服務之間相互呼叫,組成複雜的呼叫鏈路。以上的問題在鏈路呼叫中會產生放大的效果。複雜鏈路上的某一環不穩定,就可能會層層級聯,最終導致整個鏈路都不可用。因此我們需要對不穩定的弱依賴服務呼叫進行熔斷降級,暫時切斷不穩定呼叫,避免區域性不穩定因素導致整體的雪崩。熔斷降級作為保護自身的手段,通常在客戶端(呼叫端)進行配置。

6.2 降級規則之慢呼叫比例

6.2.1 概述

  • 慢呼叫比例 (SLOW_REQUEST_RATIO):選擇以慢呼叫比例作為閾值,需要設定允許的慢呼叫 RT(即最大的響應時間),請求的響應時間大於該值則統計為慢呼叫。當單位統計時長(statIntervalMs)內請求數目大於設定的最小請求數目,並且慢呼叫的比例大於閾值,則接下來的熔斷時長內請求會自動被熔斷。經過熔斷時長後熔斷器會進入探測恢復狀態(HALF-OPEN 狀態),若接下來的一個請求響應時間小於設定的慢呼叫 RT 則結束熔斷,若大於設定的慢呼叫 RT 則會再次被熔斷。

6.2.2 程式碼

package com.sunxiaping.sentinel.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-15 10:05
 */
@RestController
public class FlowLimitController {

    @GetMapping(value = "/testA")
    public String testA() {
        try {
            //睡眠1秒
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "-------testA--------";
    }

    @GetMapping(value = "/testB")
    public String testB() {
        return "~~~~~~~~testB~~~~~~~~";
    }

}

6.2.3 配置

  • 最大RT(最大響應時間)設定為200ms,即請求的響應時間大於200ms則統計為慢呼叫。單位統計時長內請求的數量大於設定的最小請求數目(最小請求數設定為5),並且慢呼叫的比例大於閾值(閾值設定為0.0),則接下來的熔斷時長內會自動被熔斷(熔斷時長設定為5s)。

6.2.4 測試

6.2.5 結論

  • 按照上述配置,永遠是1秒鐘10個程序呼叫了testA,但是我們希望200ms內處理完本地任務,並且慢調動的比例大於了所設定的閾值0.0,所以一直處於熔斷狀態,當我們關閉Jmeter時,在未來時長5秒內,斷路器一直處於開啟狀態,微服務不可用,直到5秒過去,微服務才恢復。

6.3 降級規則之異常比例

6.3.1 概述

  • 異常比例 (ERROR_RATIO):當單位統計時長(statIntervalMs)內請求數目大於設定的最小請求數目,並且異常的比例大於閾值,則接下來的熔斷時長內請求會自動被熔斷。經過熔斷時長後熔斷器會進入探測恢復狀態(HALF-OPEN 狀態),若接下來的一個請求成功完成(沒有錯誤)則結束熔斷,否則會再次被熔斷。異常比率的閾值範圍是 [0.0, 1.0],代表 0% - 100%。

6.3.2 程式碼

package com.sunxiaping.sentinel.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-15 10:05
 */
@RestController
public class FlowLimitController {

    @GetMapping(value = "/testA")
    public String testA() {
        //模擬異常
        int num = 10 / 0;
        return "-------testA--------";
    }

    @GetMapping(value = "/testB")
    public String testB() {
        return "~~~~~~~~testB~~~~~~~~";
    }

}

6.3.3 配置

  • 當單位統計時長內請求數量大於最小請求數目(設定為5),並且異常比例大於閾值(0.2),則接下來的熔斷時間內請求自動會被熔斷。經過熔斷時長後熔斷器會進入探測恢復狀態(HALF-OPEN 狀態),若接下來的一個請求成功完成(沒有錯誤)則結束熔斷,否則會再次被熔斷。

6.3.4 測試

6.3.5 結論

  • 按照上述配置,單獨訪問一次,必然訪問一次,報錯一次。開啟Jmeter後,直接高併發傳送請求,多次呼叫達到我們的配置條件了,斷路器開啟,微服務不可用了,不再報錯而是服務降級了。

6.4 異常數

6.4.1 概述

  • 當單位統計時長內的異常數目超過閾值之後會自動進行熔斷。經過熔斷時長後熔斷器會進入探測恢復狀態(HALF-OPEN 狀態),若接下來的一個請求成功完成(沒有錯誤)則結束熔斷,否則會再次被熔斷。

6.4.2 程式碼

package com.sunxiaping.sentinel.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-15 10:05
 */
@RestController
public class FlowLimitController {

    @GetMapping(value = "/testA")
    public String testA() {
        //模擬異常
        int num = 10 / 0;
        return "-------testA--------";
    }

    @GetMapping(value = "/testB")
    public String testB() {
        return "~~~~~~~~testB~~~~~~~~";
    }
}

6.4.3 配置

  • 當單位統計時長內的異常數目超過閾值(2個)之後會自動進行熔斷。經過熔斷時長後熔斷器會進入探測恢復狀態(HALF-OPEN 狀態),若接下來的一個請求成功完成(沒有錯誤)則結束熔斷,否則會再次被熔斷。

6.4.4 測試

7 熱點key限流

7.1 基本介紹

  • 何為熱點?熱點即經常訪問的資料。很多時候我們希望統計某個熱點資料中訪問頻次最高的 Top K 資料,並對其訪問進行限制。比如:

    • 商品 ID 為引數,統計一段時間內最常購買的商品 ID 並進行限制。
    • 使用者 ID 為引數,針對一段時間內頻繁訪問的使用者 ID 進行限制。
  • 熱點引數限流會統計傳入引數中的熱點引數,並根據配置的限流閾值與模式,對包含熱點引數的資源呼叫進行限流。熱點引數限流可以看做是一種特殊的流量控制,僅對包含熱點引數的資源呼叫生效。

  • Sentinel 利用 LRU 策略統計最近最常訪問的熱點引數,結合令牌桶演算法來進行引數級別的流控。熱點引數限流支援叢集模式。

7.2 @SentinelResource註解

  • 熔斷降級方法分為系統預設和使用者自定義兩種,@SentinelResource註解和Hystrix的@HystrixCommand註解類似,都是用來配置使用者自定義的熔斷降級方法。
  • @SentinelResource註解和@HystrixCommand不同的是,只處理Sentinel控制檯配置的違規情況,並不處理異常。

7.3 應用示例

7.3.1 程式碼

package com.sunxiaping.sentinel.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-15 10:05
 */
@RestController
public class FlowLimitController {

    @GetMapping(value = "/testA")
    public String testA() {
        return "-------testA--------";
    }

    @GetMapping(value = "/testB")
    public String testB() {
        return "~~~~~~~~testB~~~~~~~~";
    }

    @GetMapping(value = "/testHotKey")
    //通過blockHandler指定熔斷降級的方法,通過fallback指定觸發異常執行的降級方法
    @SentinelResource(value = "testHotKey", blockHandler = "dealTestHotKey")
    public String testHotKey(
            @RequestParam(value = "p1", required = false) String p1,
            @RequestParam(value = "p2", required = false) String p2) {

        return "testHotKey";
    }


    /**
     * 熔斷降級
     *
     * @param p1
     * @param p2
     * @param exception
     * @return
     */
    public String dealTestHotKey(String p1, String p2, BlockException exception) {

        return "--------熔斷降級-------";
    }

}

7.3.2 配置

  • 如果testHotKey的引數p1的值不是5,那麼QPS是1;如果引數p1的值是5,那麼QPS是2000。

7.3.2 測試

  • 測試http://localhost:8401/testHotKey?p1=1請求,如果每秒請求1次,不會熔斷降級,如果每次請求超過1次,會觸發熔斷降級。
  • 測試http://localhost:8401/testHotKey?p1=5請求,只要每秒請求不超過2000次,就不會熔斷降級。

8 系統規則

8.1 基本介紹

  • Sentinel 系統自適應限流從整體維度對應用入口流量進行控制,結合應用的 Load、CPU 使用率、總體平均 RT、入口 QPS 和併發執行緒數等幾個維度的監控指標,通過自適應的流控策略,讓系統的入口流量和系統的負載達到一個平衡,讓系統儘可能跑在最大吞吐量的同時保證系統整體的穩定性。

8.2 背景

  • 在開始之前,我們先了解一下系統保護的目的:

    • 保證系統不被拖垮
    • 在系統穩定的前提下,保持系統的吞吐量
  • 長期以來,系統保護的思路是根據硬指標,即系統的負載 (load1) 來做系統過載保護。當系統負載高於某個閾值,就禁止或者減少流量的進入;當 load 開始好轉,則恢復流量的進入。這個思路給我們帶來了不可避免的兩個問題:

    • load 是一個“結果”,如果根據 load 的情況來調節流量的通過率,那麼就始終有延遲性。也就意味著通過率的任何調整,都會過一段時間才能看到效果。當前通過率是使 load 惡化的一個動作,那麼也至少要過 1 秒之後才能觀測到;同理,如果當前通過率調整是讓 load 好轉的一個動作,也需要 1 秒之後才能繼續調整,這樣就浪費了系統的處理能力。所以我們看到的曲線,總是會有抖動。
    • 恢復慢。想象一下這樣的一個場景(真實),出現了這樣一個問題,下游應用不可靠,導致應用 RT 很高,從而 load 到了一個很高的點。過了一段時間之後下游應用恢復了,應用 RT 也相應減少。這個時候,其實應該大幅度增大流量的通過率;但是由於這個時候 load 仍然很高,通過率的恢復仍然不高。
  • TCP BBR 的思想給了我們一個很大的啟發。我們應該根據系統能夠處理的請求,和允許進來的請求,來做平衡,而不是根據一個間接的指標(系統 load)來做限流。最終我們追求的目標是 在系統不被拖垮的情況下,提高系統的吞吐率,而不是 load 一定要到低於某個閾值。如果我們還是按照固有的思維,超過特定的 load 就禁止流量進入,系統 load 恢復就放開流量,這樣做的結果是無論我們怎麼調引數,調比例,都是按照果來調節因,都無法取得良好的效果。

  • Sentinel 在系統自適應保護的做法是,用 load1 作為啟動自適應保護的因子,而允許通過的流量由處理請求的能力,即請求的響應時間以及當前系統正在處理的請求速率來決定。

8.3 系統規則

  • 系統保護規則是從應用級別的入口流量進行控制,從單臺機器的 load、CPU 使用率、平均 RT、入口 QPS 和併發執行緒數等幾個維度監控應用指標,讓系統儘可能跑在最大吞吐量的同時保證系統整體的穩定性。

  • 系統保護規則是應用整體維度的,而不是資源維度的,並且僅對入口流量生效。入口流量指的是進入應用的流量(EntryType.IN),比如 Web 服務或 Dubbo 服務端接收的請求,都屬於入口流量。

  • 系統規則支援以下的模式:

    • Load 自適應(僅對 Linux/Unix-like 機器生效):系統的 load1 作為啟發指標,進行自適應系統保護。當系統 load1 超過設定的啟發值,且系統當前的併發執行緒數超過估算的系統容量時才會觸發系統保護(BBR 階段)。系統容量由系統的 maxQps * minRt 估算得出。設定參考值一般是 CPU cores * 2.5
    • CPU usage(1.5.0+ 版本):當系統 CPU 使用率超過閾值即觸發系統保護(取值範圍 0.0-1.0),比較靈敏。
    • 平均 RT:當單臺機器上所有入口流量的平均 RT 達到閾值即觸發系統保護,單位是毫秒。
    • 併發執行緒數:當單臺機器上所有入口流量的併發執行緒數達到閾值即觸發系統保護。
    • 入口 QPS:當單臺機器上所有入口流量的 QPS 達到閾值即觸發系統保護。

8.4 系統規則配置介面

9 Rest實現服務降級

9.1 準備工作

  • 需要啟動Nacos和Sentinel。

9.2 服務生產者

9.2.1 匯入相關jar包的Maven座標

  • 修改部分:
<!--   Nacos     -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
  • 完整部分:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring_cloud_demo</artifactId>
        <groupId>org.sunxiaping</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud_alibaba_provider9013</artifactId>

    <dependencies>
        <!--   Nacos     -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>
</project>

9.2.2 修改配置檔案

  • application.yml
server:
  port: 9013

spring:
  application:
    name: cloud-alibaba-provider
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848 # 配置Nacos的地址

management:
  endpoints:
    web:
      exposure:
        include: '*'

9.2.3 啟動類

package com.sunxiaping.alibaba;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-11 10:54
 */
@SpringBootApplication
@EnableDiscoveryClient
public class CloudAlibabaProvider9013Application {
    public static void main(String[] args) {
        SpringApplication.run(CloudAlibabaProvider9013Application.class, args);
    }
}

9.2.4 業務邏輯

  • PaymentController.java
package com.sunxiaping.alibaba.controller;

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;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-11 10:57
 */
@RestController
public class PaymentController {

    @Value("${server.port}")
    private String serverPort;


    @GetMapping(value = "/payment/{id}")
    public String payment(@PathVariable(value = "id") Integer id) {
        return "Nacos的註冊中心的埠是:" + serverPort + ",id是:" + id;
    }

}

9.3 服務消費者

9.3.1 匯入相關jar包的Maven座標

  • 修改部分:
<!--   Nacos     -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
  • 完整部分:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring_cloud_demo</artifactId>
        <groupId>org.sunxiaping</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud_alibaba_consumer9015</artifactId>
    <dependencies>
        <!--   Nacos     -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>

</project>

9.3.2 修改配置檔案

  • application.yml:
server:
  port: 9015

spring:
  application:
    name: cloud-alibaba-consumer
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848 # 配置Nacos的地址
    sentinel:
      transport:
        # 配置Sentinel Dashboard地址
        dashboard: 127.0.0.1:8080
        # 預設8719埠,假設被佔用會自動從8719開始依次加1掃描,直到找到沒有被佔用的埠
        port: 8719
      # 取消懶載入
      eager: true

management:
  endpoints:
    web:
      exposure:
        include: '*'

9.3.3 啟動類

package com.sunxiaping.alibaba;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-11 11:34
 */
@SpringBootApplication
@EnableDiscoveryClient
public class CloudAlibabaConsumer9015Application {

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

9.3.4 業務邏輯

  • 異常工具類:
package com.sunxiaping.alibaba.utils;

import com.alibaba.csp.sentinel.slots.block.BlockException;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-21 14:58
 */
public class ExceptionUtils {

    public static String handleBlock(Integer id, BlockException ex) {
        return "熔斷降級";
    }

    public static String handleFallback(Integer id, Throwable tx) {
        return "異常降級";
    }
}
  • SpringConfig.java
package com.sunxiaping.alibaba.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-11 11:35
 */
@Configuration
public class SpringConfig {

    @LoadBalanced
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

}
  • OrderController.java
package com.sunxiaping.alibaba.controller;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.sunxiaping.alibaba.utils.ExceptionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-11 11:35
 */
@RestController
public class OrderController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping(value = "/order/{id}")
    @SentinelResource(blockHandlerClass = ExceptionUtils.class, blockHandler = "handleBlock", fallbackClass = ExceptionUtils.class, fallback = "handleFallback")
    public String order(@PathVariable(value = "id") Integer id) {
        if (4 == id) {
            throw new IllegalArgumentException("非法引數異常");
        }
        return this.restTemplate.getForObject("http://cloud-alibaba-provider" + "/payment/" + id, String.class);
    }

}

10 Feign實現服務降級

10.1 準備工作

  • 需要啟動Nacos和Sentinel。

10.2 服務生產者

10.2.1 匯入相關jar包的Maven座標

  • 修改部分:
<!--   Nacos     -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
  • 完整部分:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring_cloud_demo</artifactId>
        <groupId>org.sunxiaping</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud_alibaba_provider9013</artifactId>

    <dependencies>
        <!--   Nacos     -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>
</project>

10.2.2 修改配置檔案

  • application.yml
server:
  port: 9013

spring:
  application:
    name: cloud-alibaba-provider
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848 # 配置Nacos的地址

management:
  endpoints:
    web:
      exposure:
        include: '*'

10.2.3 啟動類

package com.sunxiaping.alibaba;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-11 10:54
 */
@SpringBootApplication
@EnableDiscoveryClient
public class CloudAlibabaProvider9013Application {
    public static void main(String[] args) {
        SpringApplication.run(CloudAlibabaProvider9013Application.class, args);
    }
}

10.2.4 業務邏輯

  • PaymentController.java
package com.sunxiaping.alibaba.controller;

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;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-11 10:57
 */
@RestController
public class PaymentController {

    @Value("${server.port}")
    private String serverPort;


    @GetMapping(value = "/payment/{id}")
    public String payment(@PathVariable(value = "id") Integer id) {
        return "Nacos的註冊中心的埠是:" + serverPort + ",id是:" + id;
    }

}

10.3 服務消費者

10.3.1 匯入相關jar包的Maven座標

  • 修改部分:
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
  • 完整部分:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring_cloud_demo</artifactId>
        <groupId>org.sunxiaping</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud_alibaba_consumer9015</artifactId>
    <dependencies>
        <!--   Nacos     -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>

</project>

10.3.2 修改配置檔案

  • application.yml
server:
  port: 9015

spring:
  application:
    name: cloud-alibaba-consumer
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848 # 配置Nacos的地址
    sentinel:
      transport:
        # 配置Sentinel Dashboard地址
        dashboard: 127.0.0.1:8080
        # 預設8719埠,假設被佔用會自動從8719開始依次加1掃描,直到找到沒有被佔用的埠
        port: 8719
      # 取消懶載入
      eager: true

# 開啟Feign對sentinel的支援
feign:
  sentinel:
    enabled: true

management:
  endpoints:
    web:
      exposure:
        include: '*'

10.3.3 啟動類

package com.sunxiaping.alibaba;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-11 11:34
 */
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class CloudAlibabaConsumer9015Application {

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

10.3.4 業務邏輯

  • PaymentFeign.java
package com.sunxiaping.alibaba.feign;

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

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-21 16:07
 */
@FeignClient(value = "cloud-alibaba-provider", fallback = PaymentFeignCallback.class)
public interface PaymentFeign {

    @GetMapping(value = "/payment/{id}")
    String payment(@PathVariable(value = "id") Integer id);

}
  • PaymentFeignCallback.java
package com.sunxiaping.alibaba.feign;

import org.springframework.stereotype.Component;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-21 16:09
 */
@Component
public class PaymentFeignCallback implements PaymentFeign {
    @Override
    public String payment(Integer id) {
        return "熔斷降級";
    }
}
  • OrderController.java
package com.sunxiaping.alibaba.controller;

import com.sunxiaping.alibaba.feign.PaymentFeign;
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;

/**
 * @author 許大仙
 * @version 1.0
 * @since 2020-10-11 11:35
 */
@RestController
public class OrderController {

    @Resource
    private PaymentFeign paymentFeign;

    @GetMapping(value = "/order/{id}")
    public String order(@PathVariable(value = "id") Integer id) {
        return this.paymentFeign.payment(id);
    }

}

11 Sentinel規則持久化

11.1 是什麼?

  • 一旦我們重啟應用,Sentinel規則將消失,生產環境需要將配置規則進行持久化。

11.2 怎麼玩?

  • 將Sentinel中的規則持久化到Nacos儲存,只要Nacos裡面的配置不刪除,那麼Sentinel中的規則將持續有效。

11.3 步驟(修改服務消費者)

11.3.1 匯入相關jar包的Maven座標

  • 修改部分:
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
  • 完整部分:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring_cloud_demo</artifactId>
        <groupId>org.sunxiaping</groupId>
        <version>1.0</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud_alibaba_consumer9015</artifactId>
    <dependencies>
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>
        <!--   Nacos     -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>
</project>

11.3.2 修改配置檔案

  • application.yml
server:
  port: 9015

spring:
  application:
    name: cloud-alibaba-consumer
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848 # 配置Nacos的地址
    sentinel:
      transport:
        # 配置Sentinel Dashboard地址
        dashboard: 127.0.0.1:8080
        # 預設8719埠,假設被佔用會自動從8719開始依次加1掃描,直到找到沒有被佔用的埠
        port: 8719
      # 將Sentinel的規則持久化到Nacos中
      datasource:
        ds1:
          nacos:
            server-addr: 127.0.0.1:8848
            dataId: ${spring.application.name}
            groupId: DEFAULT_GROUP
            data_type: json
            rule_type: flow
      # 取消懶載入
      eager: true

# 開啟Feign對sentinel的支援
feign:
  sentinel:
    enabled: true

management:
  endpoints:
    web:
      exposure:
        include: '*'

11.3.3 在Nacos中新增業務規則配置

[
    {
        "resource": "/hello", 
        "limitApp": "default", 
        "grade": 1, 
        "count": 5, 
        "strategy": 0, 
        "controlBehavior": 0, 
        "clusterMode": false 
    }
]
  • resource:資源名稱。
  • limitApp:來源應用。
  • grade:閾值型別,0表示執行緒數,1表示QPS。
  • count:單機閾值。
  • strategy:流控模式,0表示直接,1表示關聯,2表示鏈路。
  • controlBehavior:流控效果,0表示快速失敗,1表示Warm up,2表示排隊等待。
  • clusterMode:是否叢集。

11.3.4 啟動微服務消費者,重新整理Sentinel