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 達到閾值即觸發系統保護。
- Load 自適應(僅對 Linux/Unix-like 機器生效):系統的 load1 作為啟發指標,進行自適應系統保護。當系統 load1 超過設定的啟發值,且系統當前的並發線程數超過估算的系統容量時才會觸發系統保護(BBR 階段)。系統容量由系統的
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:是否集群。