1. 程式人生 > >Spring Cloud 系列之 Alibaba Sentinel 服務哨兵

Spring Cloud 系列之 Alibaba Sentinel 服務哨兵

![](https://mrhelloworld.com/resources/articles/spring/spring-cloud/sentinel/43697219-3cb4ef3a-9975-11e8-9a9c-73f4f537442d.png)   前文中我們提到 Netflix 中多項開源產品已進入維護階段,不再開發新的版本,就目前來看是沒有什麼問題的。但是從長遠角度出發,我們還是需要考慮是否有可替代產品使用。比如本文中要介紹的 Alibaba Sentinel 就是一款高效能且輕量級的流量控制、熔斷降級可替換方案。   Sentinel 官網:https://github.com/alibaba/Sentinel    **Hystrix 目前狀態**      官網提示:https://github.com/Netflix/Hystrix > Hystrix is no longer in active development, and is currently in maintenance mode. > > Hystrix 不再主動開發,當前處於維護模式。    ## Sentinel 是什麼      隨著微服務的流行,服務和服務之間的穩定性變得越來越重要。Sentinel 以流量為切入點,從流量控制、熔斷降級、系統負載保護等多個維度保護服務的穩定性。    Sentinel 具有以下特徵: - **豐富的應用場景**:Sentinel 承接了阿里巴巴近 10 年的雙十一大促流量的核心場景,例如秒殺(即突發流量控制在系統容量可以承受的範圍)、訊息削峰填谷、叢集流量控制、實時熔斷下游不可用應用等。 - **完備的實時監控**:Sentinel 同時提供實時的監控功能。您可以在控制檯中看到接入應用的單臺機器秒級資料,甚至 500 臺以下規模的叢集的彙總執行情況。 - **廣泛的開源生態**:Sentinel 提供開箱即用的與其它開源框架/庫的整合模組,例如與 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相應的依賴並進行簡單的配置即可快速地接入 Sentinel。 - **完善的 SPI 擴充套件點**:Sentinel 提供簡單易用、完善的 SPI 擴充套件介面。您可以通過實現擴充套件介面來快速地定製邏輯。例如定製規則管理、適配動態資料來源等。    **Sentinel 主要特徵**    ![](https://mrhelloworld.com/resources/articles/spring/spring-cloud/sentinel/sentinel-features-overview-en.png)    **Sentinel 開源生態**    ![](https://mrhelloworld.com/resources/articles/spring/spring-cloud/sentinel/sentinel-opensource-eco-landscape-en.png)      Sentinel 目前已經針對 Servlet、Dubbo、Spring Boot/Spring Cloud、gRPC 等進行了適配,使用者只需引入相應依賴並進行簡單配置即可非常方便地享受 Sentinel 的高可用流量防護能力。Sentinel 還為 Service Mesh 提供了叢集流量防護的能力。未來 Sentinel 還會對更多常用框架進行適配。    Sentinel 分為兩個部分: - 核心庫(Java 客戶端)不依賴任何框架/庫,能夠運行於所有 Java 執行時環境,同時對 Dubbo / Spring Cloud 等框架也有較好的支援。 - 控制檯(Dashboard)基於 Spring Boot 開發,打包後可以直接執行,不需要額外的 Tomcat 等應用容器。    ## Sentinel 的歷史    - 2012 年,Sentinel 誕生,主要功能為入口流量控制。 - 2013-2017 年,Sentinel 在阿里巴巴集團內部迅速發展,成為基礎技術模組,覆蓋了所有的核心場景。Sentinel 也因此積累了大量的流量歸整場景以及生產實踐。 - 2018 年,Sentinel 開源,並持續演進。    ## Sentinel vs Hystrix    ### Hystrix      官網:https://github.com/Netflix/Hystrix > Hystrix is a latency and fault tolerance library designed to isolate points of access to remote systems, services and 3rd party libraries, stop cascading failure and enable resilience in complex distributed systems where failure is inevitable.   Hystrix 的關注點在於隔離和熔斷為主的容錯機制,超時或被熔斷的呼叫將會快速失敗,並可以提供 fallback 機制。    ### Sentinel      官網:https://github.com/alibaba/Sentinel    Sentinel 的關注點在於: - 多樣化的流量控制 - 熔斷降級 - 系統負載保護 - 實時監控和控制檯    **Hystrix 遷移 Sentinel 方案**      Sentinel 提供了從 Hystrix 遷移到 Sentinel 的方案,官網:[https://github.com/alibaba/Sentinel/wiki/Guideline:-從-Hystrix-遷移到-Sentinel](https://github.com/alibaba/Sentinel/wiki/Guideline:-從-Hystrix-遷移到-Sentinel)    ### 總結    | | Sentinel | Hystrix | | :------------- | :--------------------------------------------- | :---------------------------- | | 隔離策略 | 訊號量隔離(併發執行緒數限流) | 執行緒池隔離/訊號量隔離 | | 熔斷降級策略 | 基於響應時間、異常比率、異常數 | 基於異常比率 | | 實時指標實現 | 滑動視窗(LeapArray) | 滑動視窗(基於 RxJava) | | 規則配置 | 支援多種資料來源 | 支援多種資料來源 | | 擴充套件性 | 多個擴充套件點 | 外掛的形式 | | 基於註解的支援 | 支援 | 支援 | | 呼叫鏈路資訊 | 支援同步呼叫 | 不支援 | | 限流 | 基於 QPS / 併發數,支援基於呼叫關係的限流 | 有限支援 | | 流量整形 | 支援慢啟動、勻速器模式 | 不支援 | | 系統負載保護 | 支援 | 不支援 | | 控制檯 | 開箱即用,可配置規則、檢視秒級監控、機器發現等 | 較為簡單 | | 常見框架的適配 | Servlet、Spring Cloud、Dubbo、gRPC 等 | Servlet、Spring Cloud Netflix |    ## Sentinel 核心    Sentinel 的使用可以分為兩個部分: - 核心庫(Java 客戶端):不依賴任何框架/庫,能夠運行於 Java 7 及以上的版本的執行時環境,同時對 Dubbo / Spring Cloud 等框架也有較好的支援(見 [主流框架適配](https://github.com/alibaba/Sentinel/wiki/主流框架的適配))。 - 控制檯(Dashboard):控制檯主要負責管理推送規則、監控、叢集限流分配管理、機器發現等。    ## Sentinel 控制檯      Sentinel 提供一個輕量級的開源控制檯,它提供機器發現以及健康情況管理、監控(單機和叢集),規則管理和推送的功能。   官網文件:[https://github.com/alibaba/Sentinel/wiki/控制檯](https://github.com/alibaba/Sentinel/wiki/控制檯)    ### 獲取控制檯      您可以從 [release 頁面](https://github.com/alibaba/Sentinel/releases) 下載最新版本的控制檯 jar 包。   您也可以從最新版本的原始碼自行構建 Sentinel 控制檯: - 下載 [控制檯](https://github.com/alibaba/Sentinel/tree/master/sentinel-dashboard) 工程 - 使用以下命令將程式碼打包成一個 fat jar: `mvn clean package`    ### 啟動控制檯      啟動命令如下,本文使用的是目前最新 1.7.1 版本: ```shell java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.7.1.jar ``` > **注意**:啟動 Sentinel 控制檯需要 JDK 版本為 1.8 及以上版本。   其中 `-Dserver.port=8080` 用於指定 Sentinel 控制檯埠為 `8080`。   從 Sentinel 1.6.0 起,Sentinel 控制檯引入基本的**登入**功能,預設使用者名稱和密碼都是 `sentinel`。可以參考 [鑑權模組文件](https://github.com/alibaba/Sentinel/wiki/控制檯#鑑權) 配置使用者名稱和密碼。 > 注:若您的應用為 Spring Boot 或 Spring Cloud 應用,您可以通過 Spring 配置檔案來指定配置,詳情請參考 [Spring Cloud Alibaba Sentinel 文件](https://github.com/spring-cloud-incubator/spring-cloud-alibaba/wiki/Sentinel)。      為了方便啟動,可以編寫一個啟動指令碼 `run.bat`: ```shell java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.7.1.jar pause ```    ### 訪問      訪問:http://localhost:8080/ ![](https://mrhelloworld.com/resources/articles/spring/spring-cloud/sentinel/image-20200131171829583.png)      輸入預設使用者名稱和密碼 `sentinel` 點選登入。至此控制檯就安裝完成了。 ![](https://mrhelloworld.com/resources/articles/spring/spring-cloud/sentinel/image-20200131172136071.png)    ## 環境準備      `sentinel-demo` 聚合工程。`SpringBoot 2.2.4.RELEASE`、`Spring Cloud Hoxton.SR1`。 - `eureka-server`:註冊中心 - `eureka-server02`:註冊中心 - `product-service`:商品服務,提供了 `/product/{id}` 介面 - `order-service-rest`:訂單服務,基於 `Ribbon` 通過 `RestTemplate` 呼叫商品服務 - `order-server-feign`:訂單服務,基於 `Feign` 通過宣告式服務呼叫商品服務    ![](https://mrhelloworld.com/resources/articles/spring/spring-cloud/sentinel/image-20200131180149055.png)    ## 客戶端接入控制檯      控制檯啟動後,客戶端需要按照以下步驟接入到控制檯: - 新增依賴 - 定義資源 - 定義規則      先把可能需要保護的資源定義好,之後再配置規則。也可以理解為,只要有了資源,我們就可以在任何時候靈活地定義各種流量控制規則。在編碼的時候,只需要考慮這個程式碼是否需要保護,如果需要保護,就將之定義為一個資源。      由於我們的專案是 Spring Cloud 專案所以藉助官方文件來進行學習。   Spring 官網文件:https://spring-cloud-alibaba-group.github.io/github-pages/greenwich/spring-cloud-alibaba.html   Github 文件:https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel    ### 新增依賴      父工程需要新增如下依賴: ```xml com.alibaba.cloud
spring-cloud-alibaba-dependencies 2.1.0.RELEASE pom import
```   子工程需要新增如下依賴: ```xml com.alibaba.cloud spring-cloud-starter-alibaba-sentinel ```    #### 配置檔案      客戶端需要啟動 Transport 模組來與 Sentinel 控制檯進行通訊。   `order-service-rest` 的 application.yml ```yml spring: cloud: sentinel: transport: port: 8719 dashboard: localhost:8080 ```   這裡的 `spring.cloud.sentinel.transport.port` 埠配置會在應用對應的機器上啟動一個 Http Server,該 Server 會與 Sentinel 控制檯做互動。比如 Sentinel 控制檯添加了一個限流規則,會把規則資料 push 給這個 Http Server 接收,Http Server 再將規則註冊到 Sentinel 中。    #### 初始化客戶端      **確保客戶端有訪問量**,Sentinel 會在**客戶端首次呼叫的時候**進行初始化,開始向控制檯傳送心跳包。   簡單的理解就是:訪問一次客戶端,Sentinel 即可完成客戶端初始化操作,並持續向控制檯傳送心跳包。    #### 訪問      首先確保 Sentinel 是啟動狀態,然後依次啟動 eureka-server,eureka-server02,product-service,order-service-rest。   多次訪問:http://localhost:9090/order/1 然後檢視控制檯`實時監控`結果如下: ![](https://mrhelloworld.com/resources/articles/spring/spring-cloud/sentinel/image-20200214154631720.png)    ### 定義資源      **資源** 是 Sentinel 中的核心概念之一。我們說的資源,可以是任何東西,服務,服務裡的方法,甚至是一段程式碼。最常用的資源是我們程式碼中的 Java 方法。Sentinel 提供了 `@SentinelResource` 註解用於定義資源,並提供了 AspectJ 的擴充套件用於自動定義資源、處理 `BlockException` 等。   官網文件:[https://github.com/alibaba/Sentinel/wiki/如何使用#定義資源](https://github.com/alibaba/Sentinel/wiki/如何使用#定義資源)    #### 註解支援      官網文件:[https://github.com/alibaba/Sentinel/wiki/註解支援](https://github.com/alibaba/Sentinel/wiki/註解支援) ```java package com.example.service.impl; import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.example.pojo.Product; import com.example.service.ProductService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; /** * 商品管理 */ @Service public class ProductServiceImpl implements ProductService { @Autowired private RestTemplate restTemplate; /** * 根據主鍵查詢商品 * * @param id * @return */ @SentinelResource(value = "selectProductById", blockHandler = "selectProductByIdBlockHandler", fallback = "selectProductByIdFallback") @Override public Product selectProductById(Integer id) { return restTemplate.getForObject("http://product-service/product/" + id, Product.class); } // 服務流量控制處理,引數最後多一個 BlockException,其餘與原函式一致。 public Product selectProductByIdBlockHandler(Integer id, BlockException ex) { // Do some log here. ex.printStackTrace(); return new Product(id, "服務流量控制處理-託底資料", 1, 2666D); } // 服務熔斷降級處理,函式簽名與原函式一致或加一個 Throwable 型別的引數 public Product selectProductByIdFallback(Integer id, Throwable throwable) { System.out.println("product-service 服務的 selectProductById 方法出現異常,異常資訊如下:" + throwable); return new Product(id, "服務熔斷降級處理-託底資料", 1, 2666D); } } ``` >
注意:註解方式埋點不支援 private 方法。    `@SentinelResource` 用於定義資源,並提供可選的異常處理和 fallback 配置項。 `@SentinelResource` 註解包含以下屬性: - `value`:資源名稱,必需項(不能為空) - `entryType`:entry 型別,可選項(預設為 `EntryType.OUT`) - `blockHandler` / `blockHandlerClass`: `blockHandler `對應處理 `BlockException` 的函式名稱,可選項。blockHandler 函式訪問範圍需要是 `public`,返回型別需要與原方法相匹配,引數型別需要和原方法相匹配並且最後加一個額外的引數,型別為 `BlockException`。blockHandler 函式預設需要和原方法在同一個類中。若希望使用其他類的函式,則可以指定 `blockHandlerClass` 為對應的類的 `Class` 物件,注意對應的函式必需為 static 函式,否則無法解析。 - `fallback`:fallback 函式名稱,可選項,用於在丟擲異常的時候提供 fallback 處理邏輯。fallback 函式可以針對所有型別的異常(除了 `exceptionsToIgnore` 裡面排除掉的異常型別)進行處理。fallback 函式簽名和位置要求: - 返回值型別必須與原函式返回值型別一致; - 方法引數列表需要和原函式一致,或者可以額外多一個 `Throwable` 型別的引數用於接收對應的異常。 - fallback 函式預設需要和原方法在同一個類中。若希望使用其他類的函式,則可以指定 `fallbackClass` 為對應的類的 `Class` 物件,注意對應的函式必需為 static 函式,否則無法解析。 - `defaultFallback`(since 1.6.0):預設的 fallback 函式名稱,可選項,通常用於通用的 fallback 邏輯(即可以用於很多服務或方法)。預設 fallback 函式可以針對所有型別的異常(除了 `exceptionsToIgnore` 裡面排除掉的異常型別)進行處理。若同時配置了 fallback 和 defaultFallback,則只有 fallback 會生效。defaultFallback 函式簽名要求: - 返回值型別必須與原函式返回值型別一致; - 方法引數列表需要為空,或者可以額外多一個 `Throwable` 型別的引數用於接收對應的異常。 - defaultFallback 函式預設需要和原方法在同一個類中。若希望使用其他類的函式,則可以指定 `fallbackClass` 為對應的類的 `Class` 物件,注意對應的函式必需為 static 函式,否則無法解析。 - `exceptionsToIgnore`(since 1.6.0):用於指定哪些異常被排除掉,不會計入異常統計中,也不會進入 fallback 邏輯中,而是會原樣丟擲。 >
注:1.6.0 之前的版本 fallback 函式只針對降級異常(`DegradeException`)進行處理,**不能針對業務異常進行處理**。   特別地,若 blockHandler 和 fallback 都進行了配置,則被限流降級而丟擲 `BlockException` 時只會進入 `blockHandler` 處理邏輯。若未配置 `blockHandler`、`fallback` 和 `defaultFallback`,則被限流降級時會將 `BlockException` **直接丟擲**(若方法本身未定義 throws BlockException 則會被 JVM 包裝一層 `UndeclaredThrowableException`)。   從 1.4.0 版本開始,註解方式定義資源支援自動統計業務異常,無需手動呼叫 `Tracer.trace(ex)` 來記錄業務異常。Sentinel 1.4.0 以前的版本需要自行呼叫 `Tracer.trace(ex)` 來記錄業務異常。    ### 定義規則      Sentinel 的所有規則都可以在**記憶體態中動態地查詢及修改,修改之後立即生效**。同時 Sentinel 也提供相關 API,供您來定製自己的規則策略。   Sentinel 支援以下幾種規則:**流量控制規則**、**熔斷降級規則**、**系統保護規則**、**來源訪問控制規則** 和 **熱點引數規則**。   官網文件:[https://github.com/alibaba/Sentinel/wiki/如何使用#規則的種類](https://github.com/alibaba/Sentinel/wiki/如何使用#規則的種類)    #### 流量控制規則      選擇 `簇點鏈路` 找到定義好的資源 `selectProductById` 並點選對應的規則按鈕進行設定。 ![](https://mrhelloworld.com/resources/articles/spring/spring-cloud/sentinel/image-20200214155100196.png)      比如我們設定一個流量控制規則,定義資源訪問的 QPS 為 1(每秒能處理查詢數目)。 ![](https://mrhelloworld.com/resources/articles/spring/spring-cloud/sentinel/image-20200214155300979.png) ![](https://mrhelloworld.com/resources/articles/spring/spring-cloud/sentinel/image-20200131213800426.png)    **測試**      快速重新整理頁面多次訪問:http://localhost:9090/order/1 結果如下: ![](https://mrhelloworld.com/resources/articles/spring/spring-cloud/sentinel/image-20200214155837546.png)    #### 熔斷降級規則    **模擬服務出錯**      修改 `order-service-rest` 專案中的核心程式碼,模擬服務出錯。 ```java package com.example.service.impl; import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.example.pojo.Product; import com.example.service.ProductService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; /** * 商品管理 */ @Service public class ProductServiceImpl implements ProductService { @Autowired private RestTemplate restTemplate; /** * 根據主鍵查詢商品 * * @param id * @return */ @SentinelResource(value = "selectProductById", blockHandler = "selectProductByIdBlockHandler", fallback = "selectProductByIdFallback") @Override public Product selectProductById(Integer id) { // 模擬查詢主鍵為 1 的商品資訊會導致異常 if (1 == id) throw new RuntimeException("查詢主鍵為 1 的商品資訊導致異常"); return restTemplate.getForObject("http://product-service/product/" + id, Product.class); } // 服務流量控制處理,引數最後多一個 BlockException,其餘與原函式一致。 public Product selectProductByIdBlockHandler(Integer id, BlockException ex) { // Do some log here. ex.printStackTrace(); return new Product(id, "服務流量控制處理-託底資料", 1, 2666D); } // 服務熔斷降級處理,函式簽名與原函式一致或加一個 Throwable 型別的引數 public Product selectProductByIdFallback(Integer id, Throwable throwable) { System.out.println("product-service 服務的 selectProductById 方法出現異常,異常資訊如下:" + throwable); return new Product(id, "服務熔斷降級處理-託底資料", 1, 2666D); } } ```      熔斷降級規則支援相應時間、異常比例、異常數三種方式。 ![](https://mrhelloworld.com/resources/articles/spring/spring-cloud/sentinel/image-20200131214554221.png)    **測試**      訪問:http://localhost:9090/order/1 結果如下: ![](https://mrhelloworld.com/resources/articles/spring/spring-cloud/sentinel/image-20200214160237861.png)    #### 動態規則擴充套件      官網文件: - [https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel#動態資料來源支援](https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel#動態資料來源支援) - [https://github.com/alibaba/Sentinel/wiki/動態規則擴充套件#示例](https://github.com/alibaba/Sentinel/wiki/動態規則擴充套件#示例)      `SentinelProperties` 內部提供了 `TreeMap` 型別的 `datasource` 屬性用於配置資料來源資訊。支援: - 檔案配置規則 - Nacos 配置規則 - ZooKeeper 配置規則 - Apollo 配置規則 - Redis 配置規則    ##### 檔案配置規則      Sentinel 支援通過本地檔案載入規則配置,使用方式如下(限流規則作為演示): ```yml spring: cloud: sentinel: datasource: ds1: file: file: classpath:flowRule.json data-type: json rule-type: flow ```      flowRule.json 對應 `com.alibaba.csp.sentinel.slots.block.RuleConstant` 各屬性。 ```json [ { "resource": "selectProductList", "count": 1, "grade": 1, "limitApp": "default", "strategy": 0, "controlBehavior": 0 } ] ```      重要屬性: | Field | 說明 | 預設值 | | --------------- | ------------------------------------------------------------ | ----------------------------- | | resource | 資源名,資源名是限流規則的作用物件 | | | count | 限流閾值 | | | grade | 限流閾值型別,QPS 模式(1)或併發執行緒數模式(0) | QPS 模式 | | limitApp | 流控針對的呼叫來源 | `default`,代表不區分呼叫來源 | | strategy | 呼叫關係限流策略:直接、鏈路、關聯 | 根據資源本身(直接) | | controlBehavior | 流控效果(直接拒絕 / 排隊等待 / 慢啟動模式),不支援按呼叫關係限流 | 直接拒絕 | | clusterMode | 是否叢集限流 | 否 |      訪問客戶端以後,重新整理控制檯,檢視流控規則如下: ![](https://mrhelloworld.com/resources/articles/spring/spring-cloud/sentinel/image-20200131221321055.png)    ## RestTemplate 支援      Spring Cloud Alibaba Sentinel 支援對 RestTemplate 呼叫的服務進行服務保護。需要在構造 RestTemplate Bean 時新增 `@SentinelRestTemplate` 註解。    ### 啟動類      OrderServiceRestApplication.java ```java package com.example; import com.alibaba.cloud.sentinel.annotation.SentinelRestTemplate; import com.example.exception.ExceptionUtil; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication public class OrderServiceRestApplication { @Bean @LoadBalanced @SentinelRestTemplate(blockHandler = "handleException", blockHandlerClass = ExceptionUtil.class, fallback = "fallback", fallbackClass = ExceptionUtil.class) public RestTemplate restTemplate() { return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(OrderServiceRestApplication.class, args); } } ```    ### 服務熔斷處理類      ExceptionUtil.java 必須使用靜態方法。 ```java package com.example.exception; import com.alibaba.cloud.sentinel.rest.SentinelClientHttpResponse; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.alibaba.fastjson.JSON; import com.example.pojo.Product; import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpRequestExecution; import org.springframework.http.client.ClientHttpResponse; public class ExceptionUtil { // 服務流量控制處理 public static ClientHttpResponse handleException(HttpRequest request, byte[] body, ClientHttpRequestExecution execution, BlockException exception) { exception.printStackTrace(); return new SentinelClientHttpResponse( JSON.toJSONString(new Product(1, "服務流量控制處理-託底資料", 1, 2666D))); } // 服務熔斷降級處理 public static ClientHttpResponse fallback(HttpRequest request, byte[] body, ClientHttpRequestExecution execution, BlockException exception) { exception.printStackTrace(); return new SentinelClientHttpResponse( JSON.toJSONString(new Product(1, "服務熔斷降級處理-託底資料", 1, 2666D))); } } ```    ### 訪問      控制檯設定流量控制規則,定義資源訪問的 QPS 為 1(每秒能處理查詢數目)。   快速重新整理頁面多次訪問:http://localhost:9090/order/1 結果如下: ![](https://mrhelloworld.com/resources/articles/spring/spring-cloud/sentinel/image-20200214155837546.png)    ## OpenFeign 支援      其實不管是 Hystrix 還是 Sentinel 對於 Feign 的支援,核心程式碼基本上是一致的,只需要修改依賴和配置檔案即可。    ### 新增依賴    ```xml org.springframework.cloud spring-cloud-starter-openfeign com.alibaba.cloud spring-cloud-starter-alibaba-sentinel ```    ### 開啟 Sentinel    ```yml server: port: 9091 # 埠 spring: application: name: order-service-feign # 應用名稱 cloud: sentinel: transport: port: 8719 dashboard: localhost:8080 # 配置 Eureka Server 註冊中心 eureka: instance: prefer-ip-address: true # 是否使用 ip 地址註冊 instance-id: ${spring.cloud.client.ip-address}:${server.port} # ip:port client: service-url: # 設定服務註冊中心地址 defaultZone: http://localhost:8761/eureka/,http://localhost:8762/eureka/ # feign 開啟 sentinel 支援 feign: sentinel: enabled: true ```    ### 熔斷降級      ProductServiceFallback.java ```java package com.example.fallback; import com.example.pojo.Product; import com.example.service.ProductService; import feign.hystrix.FallbackFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; /** * 服務熔斷降級處理可以捕獲異常 */ @Component public class ProductServiceFallbackFactory implements FallbackFactory { // 獲取日誌,在需要捕獲異常的方法中進行處理 Logger logger = LoggerFactory.getLogger(ProductServiceFallbackFactory.class); @Override public ProductService create(Throwable throwable) { return new ProductService() { @Override public Product selectProductById(Integer id) { logger.error("product-service 服務的 selectProductById 方法出現異常,異常資訊如下:" + throwable); return new Product(id, "託底資料", 1, 2666D); } }; } } ```    ### 消費服務      ProductService.java ```java package com.example.service; import com.example.fallback.ProductServiceFallbackFactory; import com.example.pojo.Product; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; // 宣告需要呼叫的服務 @FeignClient(value = "product-service", fallbackFactory = ProductServiceFallbackFactory.class) public interface ProductService { /** * 根據主鍵查詢商品 * * @param id * @return */ @GetMapping("/product/{id}") Product selectProductById(@PathVariable("id") Integer id); } ```      OrderServiceImpl.java ```java package com.example.service.impl; import com.example.pojo.Order; import com.example.service.OrderService; import com.example.service.ProductService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.Arrays; @Service public class OrderServiceImpl implements OrderService { @Autowired private ProductService productService; /** * 根據主鍵查詢訂單 * * @param id * @return */ @Override public Order selectOrderById(Integer id) { return new Order(id, "order-001", "中國", 2666D, Arrays.asList(productService.selectProductById(1))); } } ```    ### 控制層    ```java package com.example.controller; import com.example.pojo.Order; import com.example.service.OrderService; 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.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/order") public class OrderController { @Autowired private OrderService orderService; /** * 根據主鍵查詢訂單 * * @param id * @return */ @GetMapping("/{id}") public Order selectOrderById(@PathVariable("id") Integer id) { return orderService.selectOrderById(id); } } ```    ### 啟動類    ```java package com.example; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; // 開啟 FeignClients 註解 @EnableFeignClients @SpringBootApplication public class OrderServiceFeignApplication { public static void main(String[] args) { SpringApplication.run(OrderServiceFeignApplication.class, args); } } ```    ### 測試      控制檯資訊如下: ![](https://mrhelloworld.com/resources/articles/spring/spring-cloud/sentinel/image-20200131233524521.png)      新增流量控制規則,定義資源訪問的 QPS 為 1(每秒能處理查詢數目)。 ![](https://mrhelloworld.com/resources/articles/spring/spring-cloud/sentinel/image-20200131233925164.png)      快速重新整理頁面多次訪問:http://localhost:9091/order/1 結果如下: ![](https://mrhelloworld.com/resources/articles/spring/spring-cloud/sentinel/image-20200214161610742.png)      或者關閉服務提供者,訪問:http://localhost:9091/order/1 結果如下: ![](https://mrhelloworld.com/resources/articles/spring/spring-cloud/sentinel/image-20200214161610742.png)   至此 Sentinel 服務哨兵知識點就講解結束了。 ![](https://mrhelloworld.com/resources/articles/articles_bottom/end02.gif)   本文采用 `知識共享「署名-非商業性使用-禁止演繹 4.0 國際」許可協議`。   大家可以通過 `分類` 檢視更多關於 `Spring Cloud` 的文章。