微服務Spring Cloud Alibaba簡單筆記

来源:https://www.cnblogs.com/ccvc/archive/2023/02/28/17166510.html
-Advertisement-
Play Games

Nacos Nacos體系架構 領域模型 Nacos 領域模型描述了服務與實例之間的邊界和層級關係。Nacos 的服務領域模型是以“服 務”為維度構建起來的,這個服務並不是指集群中的單個伺服器,而是指微服務的服務名。 “服務”是 Nacos 中位於最上層的概念,在服務之下,還有集群和實例的概念。 服 ...


Nacos

Nacos體系架構

領域模型

Nacos 領域模型描述了服務與實例之間的邊界和層級關係。Nacos 的服務領域模型是以“服 務”為維度構建起來的,這個服務並不是指集群中的單個伺服器,而是指微服務的服務名。

“服務”是 Nacos 中位於最上層的概念,在服務之下,還有集群和實例的概念。

  • 服務

    在服務這個層級上可以配置元數據和服務保護閾值等信息。服務閾值是一個 0~1 之間的 數字,當服務的健康實例數與總實例的比例小於這個閾值的時候,說明能提供服務的機器已經 沒多少了。這時候 Nacos 會開啟服務保護模式,不再主動剔除服務實例,同時還會將不健康 的實例也返回給消費者。

  • 集群

    一個服務由很多服務實例組成,在每個服務實例啟動的時候,可以設置它所屬的集群,在 集群這個層級上,也可以配置元數據。除此之外,還可以為持久化節點設置健康檢查 模式。

    所謂持久化節點,是一種會保存到 Nacos 服務端的實例,即便該實例的客戶端進程沒有在運 行,實例也不會被服務端刪除,只不過 Nacos 會將這個持久化節點狀態標記為不健康, Nacos 可以採用一種“主動探活”的方式來對持久化節點做健康檢查。

    除了持久化節點以外,大部分服務節點在 Nacos 中以“臨時節點”的方式存在,它是預設的 服務註冊方式,從名字中就可以看出,這種節點不會被持久化保存在 Nacos 伺服器,臨 時節點通過主動發送 heartbeat 請求向伺服器報送自己的狀態。

  • 實例

    這裡所說的實例就是指服務節點,可以在 Nacos 控制台查看每個實例的 IP 地址和埠、 編輯實例的元數據信息、修改它的上線 / 下線狀態或者配置路由權重等等。

在這三個層級上都有“元數據”這一數據結構,可以把它理解為一組包含了服務 描述信息(如服務版本等)和自定義標簽的數據集合。Client 端通過服務發現技術可以獲取到 每個服務實例的元數據,可以將自定義的屬性加入到元數據併在 Client 端實現某些定製化 的業務場景。

數據模型

Nacos 的數據模型有三個層次結構,分別是 Namespace、Group 和 Service/DataId。
在這裡插入圖片描述

  • Namespace:即命名空間,它是最頂層的數據結構,可以用它來區分開發環境、生產 環境等不同環境。預設情況下,所有服務都部署到一個叫做“public”的公共命名空間;
  • Group:在命名空間之下有一個分組結構,預設情況下所有微服務都屬於 “DEFAULT_GROUP”這個分組,不同分組間的微服務是相互隔離的;
  • Service/DataID:在 Group 分組之下,就是具體的微服務了,比如訂單服務、商品服務等等。

通過 Namespace + Group + Service/DataID,就可以精准定位到一個具體的微服務

Nacos 基本架構

Nacos 的核心功能有兩個,一個是 Naming Service,用來做服務發現的模塊;另 一個是 Config Service,用來提供配置項管理、動態更新配置和元數據的功能

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-OFQ0pyt6-1677596762818)(C:\Users\0\AppData\Roaming\Typora\typora-user-images\image-20230226225644390.png)]

Provider APP 和 Consumer APP 通過 Open API 和 Nacos 服務 器的核心模塊進行通信。這裡的 Open API 是一組對外暴露的 RESTful 風格的 HTTP 介面。

在 Nacos 和核心模塊里,Naming Service 提供了將對象和實體的“名字”映射到元數據的 功能,這是服務發現的基礎功能之一。

Nacos 還有一個相當重要的模塊:Nacos Core 模塊。它可以提供一系列的平臺基礎功能, 是支撐 Nacos 上層業務場景的基石

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-49FknCy3-1677596762818)(E:\BaiduNetdiskDownload\180SpringCloud微服務項目實戰\images\472384\d0c78d0c0f2bb72c45788a5c2d423512.jpg)]

Nacos集群環境搭建

Nacos Server 的安裝包可以從 Alibaba 官方 GitHub 中的Release 頁面下載。

下載完成後,可以在本地將 Nacos Server 壓縮包解壓,並將解壓後的目錄名改為“nacos-cluster1”,再複製一份同樣的文件到 nacos-cluster2,以此來模擬一個由兩台 Nacos Server 組成的集群。

修改啟動項參數

Nacos Server 的啟動項位於 conf 目錄下的 application.properties 文件里,需要修改服務啟動埠和資料庫連接

Nacos Server 的啟動埠由 server.port 屬性指定,預設埠是 8848。在 nacos-cluster1 中仍然使用 8848 作為預設埠,需要把 nacos-cluster2 中的埠號改為 8948

在預設情況下,Nacos Server 會使用 Derby 作為數據源,用於保存配置管理數據。將 Nacos Server 的數據源遷移到更加穩定的 MySQL 資料庫中,需要修改三處 Nacos Server 的資料庫配置。

指定數據源:spring.datasource.platform=mysql 將這行註釋放開;

指定 DB 實例數:放開 db.num=1 這一行的註釋;

修改 JDBC 連接串:db.url.0 指定了資料庫連接字元串,db.user.0 和 db.password.0 分別指定了連接資料庫的用戶名和密碼

創建資料庫表

Nacos 已經把建表語句放在解壓後的 Nacos Server 安裝目錄中下的 conf 文件夾里

添加集群機器列表

Nacos Server 可以從一個本地配置文件中獲取所有的 Server 地址信息,從而實現伺服器之 間的數據同步。

在 Nacos Server 的 conf 目錄下創建 cluster.conf 文件,並將 nacos-cluster1 和 nacos-cluster2 這兩台伺服器的 IP 地址 + 埠號添加到文件中。

## 註意,這裡的IP不能是localhost或者127.0.0.1
192.168.1.100:8848
192.168.1.100:8948

啟動 Nacos Server

通過 -m standalone 參數,可以單機模式啟動。

Nacos 的啟動腳本位於安裝目錄下的 bin 文件夾,其中 Windows 操作系統對應的啟動腳本和關閉腳本分別是 startup.cmd 和 shutdown.cmd, Mac 和 Linux 系統對應的啟動和關閉腳本是 startup.sh 和 shutdown.sh。

登錄 Nacos 控制台

使用 Nacos 預設創建好的用戶 nacos 登錄系統,用戶名和密碼都是 nacos。

為了驗證集群環境處於正常狀態,可以在左側導航欄中打開“集群管理”下的“節點列表” 頁面,在這個頁面上顯示了集群環境中所有的 Nacos Server 節點以及對應的狀態,它們的節點狀態都是綠色的“UP”,這表示搭建的集群環境一切正常。

在實際的項目中,如果某個微服務 Client 要連接到 Nacos 集群做服務註冊,並不會把 Nacos 集群中的所有伺服器都配置在 Client 中,否則每次 Nacos 集群增加或刪除了節點, 都要對所有 Client 做一次代碼變更並重新發佈。

常見的一個做法是提供一個 VIP URL 給到 Client,VIP URL 是一個虛擬 IP 地址,可以把 真實的 Nacos 伺服器地址列表“隱藏”在虛擬 IP 後面,客戶端只需要連接到虛 IP 即可,由 提供虛 IP 的組件負責將請求轉發給背後的伺服器列表。這樣一來,即便 Nacos 集群機器數量 發生了變動,也不會對客戶端造成任何感知。

提供虛 IP 的技術手段有很多,比如通過搭建 Nginx+LVS 或者 keepalived 技術實現高可用集群。

將服務提供者註冊到 Nacos 伺服器

添加 Nacos 依賴項

Spring Boot、Spring Cloud 和 Spring Cloud Alibaba 三者之間有嚴格的版本匹配關係

版本說明: link

將 Spring Cloud Alibaba 和 Spring Cloud 的依賴項版本添加到頂層項目下的 pom.xml 文件中。

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>2020.0.1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>2021.1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
    <!-- 省略部分代碼 -->
</dependencyManagement>

定義了組件的大版本之後,就可以直接把 Nacos 的依賴項加入到兩個子模塊的 pom.xml 文件中

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

在添加完依賴項之後,就可以通過配置項開啟 Nacos 的服務治理功能了。Spring Cloud 各個組件都採用了自動裝配器實現了輕量級的組件集成功能,只需要幾行配置,剩下的初始化工作都可以交給背後的自動裝配器來實現。

Nacos 自動裝配原理

在 Spring Cloud 稍早一些的版本中,需要在啟動類上添加 @EnableDiscoveryClient 註 解開啟服務治理功能,而在新版本的 Spring Cloud 中,這個註解不再是一個必須的步驟, 們只需要通過配置項就可以開啟 Nacos 的功能。

們將 Nacos 依賴項添加到項目中,同時也引入了 Nacos 自帶的自動裝配器,比如下麵這幾 個被引入的自動裝配器就掌管了 Nacos 核心功能的初始化任務。

NacosDiscoveryAutoConfiguration:服務發現功能的自動裝配器,它主要做兩件事 兒:載入 Nacos 配置項,聲明 NacosServiceDiscovery 類用作服務發現;

NacosServiceAutoConfiguration:聲明核心服務治理類 NacosServiceManager,它可以通過 service id、group 等一系列參數獲取已註冊的服務列表;

NacosServiceRegistryAutoConfiguration:Nacos 服務註冊的自動裝配器。

添加 Nacos 配置項

spring:
    cloud:
        nacos:
            discovery:
            # Nacos的服務註冊地址,可以配置多個,逗號分隔
            server-addr: localhost:8848
            # 服務註冊到Nacos上的名稱,一般不用配置
            service: coupon-customer-serv
            # nacos客戶端向服務端發送心跳的時間間隔,時間單位其實是ms
            heart-beat-interval: 5000
            # 服務端沒有接受到客戶端心跳請求就將其設為不健康的時間間隔,預設為15s
            # 註:推薦值該值為15s即可,如果有的業務線希望服務下線或者出故障時希望儘快被髮現,可以適
            heart-beat-timeout: 20000
            # 元數據部分 - 可以自己隨便定製
            metadata:
                mydata: abc
            # 客戶端在啟動時是否讀取本地配置項(一個文件)來獲取服務列表
            # 註:推薦該值為false,若改成true。則客戶端會在本地的一個
            # 文件中保存服務信息,當下次宕機啟動時,會優先讀取本地的配置對外提供服務。
            naming-load-cache-at-start: false
            # 命名空間ID,Nacos通過不同的命名空間來區分不同的環境,進行數據隔離,
            namespace: dev
            # 創建不同的集群
            cluster-name: Cluster-A
            # [註意]兩個服務如果存在上下游調用關係,必須配置相同的group才能發起訪問
            group: myGroup
            # 向註冊中心註冊服務,預設為true
            # 如果只消費服務,不作為服務提供方,倒是可以設置成false,減少開銷
            register-enabled: true

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-ri6Gpvkn-1677596762819)(E:\BaiduNetdiskDownload\180SpringCloud微服務項目實戰\images\473988\bd3383d12b43a35cfc3c240386c3e0f8.jpg)]

Namespace 可以用作環境隔離或者多租戶隔離,其中:

環境隔離:比如設置三個命名空間 production、pre-production 和 dev,分別表示生產 環境、預發環境和開發環境,如果一個微服務註冊到了 dev 環境,那麼他無法調用其他環 境的服務,因為服務發現機制只會獲取到同樣註冊到 dev 環境的服務列表。如果未指定 namespace 則服務會被註冊到 public 這個預設 namespace 下。

多租戶隔離:即 multi-tenant 架構,通過為每一個用戶提供獨立的 namespace 以實現租 戶與租戶之間的環境隔離。

Group 的使用場景非常靈活,列舉幾個:

環境隔離:在多租戶架構之下,由於 namespace 已經被用於租戶隔離,為了實現同一個租 戶下的環境隔離,可以使用 group 作為環境隔離變數。

線上測試:對於涉及到上下游多服務聯動的場景,將線上已部署的待上下游測服務的 group 設置為“group-A”,由於這是一個新的獨立分組,所以線上的用戶流量不會導向 到這個 group。這樣一來,開發人員就可以在不影響線上業務的前提下,通過發送測試請 求到“group-A”的機器完成線上測試。

什麼是單元封閉呢?為了保證業務的高可用性,通常會把同一個服務部署在 不同的物理單元(比如張北機房、杭州機房、上海機房),當某個中心機房出現故障的時 候,可以在很短的時間內把用戶流量切入其他單元機房。由於同一個單元內的伺服器資 源通常部署在同一個物理機房,因此本單元內的服務調用速度最快,而跨單元的服務調用將 要承擔巨大的網路等待時間。這種情況下,可以為同一個單元的服務設置相同的 group,使微服務調用封閉在當前單元內,提高業務響應速度。

服務調用

服務消費者添加Nacos依賴項和配置信息

<!-- Nacos服務發現組件 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<!-- 負載均衡組件 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

<!-- webflux服務調用 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
  • spring-cloud-starter-loadbalancer:Spring Cloud御用負載均衡組件Loadbalancer,用來代替已經進入維護狀態的Netflix Ribbon組件。會在下一課帶深入瞭解Loadbalancer的功能,今天只需要簡單瞭解下它的用法就可以了;
  • spring-boot-starter-webflux:Webflux是Spring Boot提供的響應式編程框架,響應式編程是基於非同步和事件驅動的非阻塞程式。Webflux實現了Reactive Streams規範,內置了豐富的響應式編程特性。今天將用Webflux組件中一個叫做WebClient的小工具發起遠程服務調用。

Nacos服務發現底層實現

Nacos Client通過一種 主動輪詢 的機制從Nacos Server獲取服務註冊信息,包括地址列表、group分組、cluster名稱等一系列數據。簡單來說,Nacos Client會開啟一個本地的定時任務,每間隔一段時間,就嘗試從Nacos Server查詢服務註冊表,並將最新的註冊信息更新到本地。這種方式也被稱之為“Pull”模式,即客戶端主動從服務端拉取的模式。

負責拉取服務的任務是UpdateTask類,它實現了Runnable介面。Nacos以開啟線程的方式調用UpdateTask類中的run方法,觸發本地的服務發現查詢請求。

UpdateTask這個類是HostReactor的一個內部類,

在UpdateTask的源碼中,它通過調用updateService方法實現了服務查詢和本地註冊表更新,在每次任務執行結束的時候,在結尾處它通過finally代碼塊設置了下一次executor查詢的時間,周而複始迴圈往複。

OpenFeign

OpenFeign提供了一種聲明式的遠程調用介面,它可以大幅簡化遠程調用的編程體驗。

OpenFeign使用了一種“動態代理”技術來封裝遠程服務調用的過程,遠程服務調用的信息被寫在了FeignClient介面中

OpenFeign的動態代理

在項目初始化階段,OpenFeign會生成一個代理類,對所有通過該介面發起的遠程調用進行動態代理。

![[,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Pyah4TJ8-1677596762819)(C:\Users\

上圖中的步驟1到步驟3是在項目啟動階段載入完成的,只有第4步“調用遠程服務”是發生在項目的運行階段。

首先,在項目啟動階段, OpenFeign框架會發起一個主動的掃包流程,從指定的目錄下掃描並載入所有被@FeignClient註解修飾的介面。

然後, OpenFeign會針對每一個FeignClient介面生成一個動態代理對象,即圖中的FeignProxyService,這個代理對象在繼承關係上屬於FeignClient註解所修飾的介面的實例。

接下來, 這個動態代理對象會被添加到Spring上下文中,並註入到對應的服務里,也就是圖中的LocalService服務。

最後, LocalService會發起底層方法調用。實際上這個方法調用會被OpenFeign生成的代理對象接管,由代理對象發起一個遠程服務調用,並將調用的結果返回給LocalService。

OpenFeign是如何通過動態代理技術創建代理對象的?

在這裡插入圖片描述

  1. 項目載入:在項目的啟動階段, EnableFeignClients註解 扮演了“啟動開關”的角色,它使用Spring框架的 Import註解 導入了FeignClientsRegistrar類,開始了OpenFeign組件的載入過程。
  2. 掃包: FeignClientsRegistrar 負責FeignClient介面的載入,它會在指定的包路徑下掃描所有的FeignClients類,並構造FeignClientFactoryBean對象來解析FeignClient介面。
  3. 解析FeignClient註解: FeignClientFactoryBean 有兩個重要的功能,一個是解析FeignClient介面中的請求路徑和降級函數的配置信息;另一個是觸發動態代理的構造過程。其中,動態代理構造是由更下一層的ReflectiveFeign完成的。
  4. 構建動態代理對象:ReflectiveFeign 包含了OpenFeign動態代理的核心邏輯,它主要負責創建出FeignClient介面的動態代理對象。ReflectiveFeign在這個過程中有兩個重要任務,一個是解析FeignClient介面上各個方法級別的註解,將其中的遠程介面URL、介面類型(GET、POST等)、各個請求參數等封裝成元數據,併為每一個方法生成一個對應的MethodHandler類作為方法級別的代理;另一個重要任務是將這些MethodHandler方法代理做進一步封裝,通過Java標準的動態代理協議,構建一個實現了InvocationHandler介面的動態代理對象,並將這個動態代理對象綁定到FeignClient介面上。這樣一來,所有發生在FeignClient介面上的調用,最終都會由它背後的動態代理對象來承接。

MethodHandler的構建過程涉及到了複雜的元數據解析,OpenFeign組件將FeignClient介面上的各種註解封裝成元數據,並利用這些元數據把一個方法調用“翻譯”成一個遠程調用的Request請求。

那麼上面說到的“元數據的解析”是如何完成的呢?它依賴於OpenFeign組件中的Contract協議解析功能。Contract是OpenFeign組件中定義的頂層抽象介面,它有一系列的具體實現。

專門用來解析Spring MVC標簽的SpringMvcContract類的繼承結構是SpringMvcContract->BaseContract->Contract。

OpenFeign的工作流程的重點是 動態代理機制。OpenFeing通過Java動態代理生成了一個“代理類”,這個代理類將介面調用轉化成為了一個遠程服務調用。

FeignClientsRegistrar是OpenFeign初始化的起點

實現服務間調用功能

把依賴項spring-cloud-starter-OpenFeign添加到子模塊內的pom.xml文件中。

<!-- OpenFeign組件 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

在介面上聲明瞭一個FeignClient註解,它專門用來標記被OpenFeign托管的介面。

@FeignClient(value = "coupon-template-serv", path = "/template")
public interface TemplateService {
    // 讀取優惠券
    @GetMapping("/getTemplate")
    CouponTemplateInfo getTemplate(@RequestParam("id") Long id);

    // 批量獲取
    @GetMapping("/getBatch")
    Map<Long, CouponTemplateInfo> getTemplateInBatch(@RequestParam("ids") Collection<Long> ids);
}

在FeignClient註解中聲明的value屬性是目標服務的名稱,需要確保這裡的服務名稱和Nacos伺服器上顯示的服務註冊名稱是一樣的。

配置OpenFeign的載入路徑

@EnableFeignClients(basePackages = {"com.xxx"})
public class Application {

}

在EnableFeignClients註解的basePackages屬性中定義了一個com.xxx的包名,這個註解就會告訴OpenFeign在啟動項目的時候做一件事兒:找到所有位於com.xxx包路徑(包括子package)之下使用FeignClient修飾的介面,然後生成相關的代理類並添加到Spring的上下文中。這樣才能夠在項目中用Autowired註解註入OpenFeign介面。

日誌信息列印

服務請求的入參和出參是分析和排查問題的重要線索。為了獲得服務請求的參數和返回值,經常使用的一個做法就是 列印日誌

首先,需要在配置文件中 指定FeignClient介面的日誌級別為Debug。這樣做是因為OpenFeign組件預設將日誌信息以debug模式輸出,而預設情況下Spring Boot的日誌級別是Info

接下來,還需要在應用的上下文中使用代碼的方式 聲明Feign組件的日誌級別。這裡的日誌級別並不是傳統意義上的Log Level,它是OpenFeign組件自定義的一種日誌級別,用來控制OpenFeign組件嚮日志中寫入什麼內容。

@Bean
Logger.Level feignLogger() {
    return Logger.Level.FULL;
}

OpenFeign總共有四種不同的日誌級別

  • NONE:不記錄任何信息,這是OpenFeign預設的日誌級別;
  • BASIC:只記錄服務請求的URL、HTTP Method、響應狀態碼(如200、404等)和服務調用的執行時間;
  • HEADERS:在BASIC的基礎上,還記錄了請求和響應中的HTTP Headers;
  • FULL:在HEADERS級別的基礎上,還記錄了服務請求和服務響應中的Body和metadata,FULL級別記錄了最完整的調用信息。

超時判定

超時判定是一種保障可用性的手段。

為了隔離下游介面調用超時所帶來的的影響,可以在程式中設置一個 超時判定的閾值,一旦下游介面的響應時間超過了這個閾值,那麼程式會自動取消此次調用並返回一個異常。

feign:
  client:
    config:
      # 全局超時配置
      default:
        # 網路連接階段1秒超時
        connectTimeout: 1000
        # 服務請求響應階段5秒超時
        readTimeout: 5000
      # 針對某個特定服務的超時配置
      coupon-template-serv:
        connectTimeout: 1000
        readTimeout: 2000

降級

降級邏輯是在遠程服務調用發生超時或者異常(比如400、500 Error Code)的時候,自動執行的一段業務邏輯。

OpenFeign實現Client端的服務降級相比於Sentinel而言 更加輕量級且容易實現, 足以滿足一些簡單的服務降級業務需求。

OpenFeign對服務降級的支持是藉助Hystrix組件實現的,由於Hystrix已經從Spring Cloud組件庫中被移除,所以要在pom文件中手動添加hystrix項目的依賴。

<!-- hystrix組件,專門用來演示OpenFeign降級 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    <version>2.2.10.RELEASE</version>
    <exclusions>
        <!-- 移除Ribbon負載均衡器,避免衝突 -->
        <exclusion>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-netflix-ribbon</artifactId>
        </exclusion>
    </exclusions>
</dependency>

OpenFeign支持兩種不同的方式來指定降級邏輯,一種是定義fallback類,另一種是定義fallback工廠。

通過fallback類實現降級是最為簡單的一種途徑,如果想要為FeignClient介面指定一段降級流程,可以定義一個降級類並實現介面,併在介面中指定為降級類。

@FeignClient(value = "coupon-template-serv", path = "/template",
       // 通過fallback指定降級邏輯
       fallback = TemplateServiceFallback.class)

如果想要在降級方法中獲取到 異常的具體原因,那麼就要藉助 fallback工廠 的方式來指定降級邏輯了。按照OpenFeign的規範,自定義的fallback工廠需要實現FallbackFactory介面

@FeignClient(value = "coupon-template-serv", path = "/template",
        // 通過抽象工廠來定義降級邏輯
        fallbackFactory = TemplateServiceFallbackFactory.class)

配置中心

分散式配置中心在配置管理方面發揮的作用

高可用性: 微服務組件的高可用性是首要目標。配置中心並不是一個中心化的單點應用,而是一個通過集群對外提供服務的組件。在一致性演算法的基礎上,集群中各個節點之間會互相同步配置數據,或者從統一數據源讀取配置數據。即便個別節點掛掉,也不影響整個集群的可用性;

環境隔離特性:Nacos支持通過Namespace屬性指定當前配置項所在的環境,可以為自己的應用系統創建開發環境、預發環境和生產環境,不同環境之間的配置文件是相互隔離的;

多格式支持:Nacos支持多種不同格式的配置內容,可以使用純文本、JSON、XML、YAML和Properties多種文件尾碼;

訪問控制:Nacos實現了許可權管理功能,可以在控制台創建用戶賬號和許可權組,限制某個賬號可以訪問哪些命名空間,並配置賬號的讀寫許可權(只讀、只寫、讀寫)。通過這種方式,可以保障敏感信息(如資料庫用戶名和密碼)的安全;

職責分離:配置項從jar包中抽離了出來,修改配置項再也不需要重新編譯打包應用程式了,完美實現了配置項管理與業務代碼之間的職責分離;

版本控制和審計功能:配置項也是一種代碼,而且配置bug往往比代碼中的bug造成的影響更大。因此,在微服務架構中需要確保配置中心具備完善的版本控制和審計功能

Nacos還可以支持 多文件源讀取以及運行期配置變更。尤其是 動態變更推送,更是微服務架構下不可或缺的配置管理能力。

添加依賴項

<!-- 添加Nacos Config配置項 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>

<!-- 讀取bootstrap文件 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>

Nacos配置中心的連接信息需要配置在bootstrap文件,而非application.yml文件中。在Spring Cloud 2020.0.0版本之後,bootstrap文件不會被自動載入,需要主動添加依賴項,來開啟bootstrap的自動載入流程。

為什麼集成Nacos配置中心必須用到bootstrap配置文件呢?為了保證其他應用能夠正常啟動,必須 在其它組件初始化之前從Nacos讀到所有配置項,之後再將獲取到的配置項用於後續的初始化流程。

添加本地Nacos Config配置項

需要在bootstrap.yml文件中添加一些Nacos Config配置項

spring:
  # 必須把name屬性從application.yml遷移過來,否則無法動態刷新
  application:
    name: coupon-customer-serv
  cloud:
    nacos:
      config:
        # nacos config伺服器的地址
        server-addr: localhost:8848
        file-extension: yml
        # prefix: 文件名首碼,預設是spring.application.name
        # 如果沒有指定命令空間,則預設命令空間為PUBLIC
        namespace: dev
        # 如果沒有配置Group,則預設值為DEFAULT_GROUP
        group: DEFAULT_GROUP
        # 從Nacos讀取配置項的超時時間
        timeout: 5000
        # 長輪詢超時時間
        config-long-poll-timeout: 10000
        # 輪詢的重試時間
        config-retry-time: 2000
        # 長輪詢最大重試次數
        max-retry: 3
        # 開啟監聽和自動刷新
        refresh-enabled: true
        # Nacos的擴展配置項,數字越大優先順序越高
        extension-configs:
          - dataId: redis-config.yml
            group: EXT_GROUP
            # 動態刷新
            refresh: true
          - dataId: rabbitmq-config.yml
            group: EXT_GROUP
            refresh: true

長輪詢機制 的工作原理

當Client向Nacos Config服務端發起一個配置查詢請求時,服務端並不會立即返回查詢結果,而是會將這個請求hold一段時間。如果在這段時間內有配置項數據的變更,那麼服務端會觸發變更事件,客戶端將會監聽到該事件,並獲取相關配置變更;如果這段時間內沒有發生數據變更,那麼在這段“hold時間”結束後,服務端將釋放請求。

採用長輪詢機制可以降低多次請求帶來的網路開銷,並降低更新配置項的延遲。

動態配置推送

使用@Value註解將Nacos配置中心裡的屬性註入進來。給屬性設置一個預設值,這樣做的目的是加一層容錯機制。即便Nacos Config連接異常無法獲取配置項,應用程式也可以使用預設值完成啟動載入。

最後,在類頭上添加一個RefreshScope註解,有了這個註解,Nacos Config中的屬性變動就會動態同步到當前類的變數中。如果不添加RefreshScope註解,即便應用程式監聽到了外部屬性變更,那麼類變數的值也不會被刷新。

RefreshScope註解

為了實現動態刷新配置,主要就是想辦法達成以下兩個核心目標:

  1. 讓Spring容器重新載入Environment環境配置變數
  2. Spring Bean重新創建生成

@RefreshScope主要就是基於@Scope註解的作用域代理的基礎上進行擴展實現的,加了@RefreshScope註解的類,在被Bean工廠創建後會加入自己的refresh scope 這個Bean緩存中,後續會優先從Bean緩存中獲取,當配置中心發生了變更,會把變更的配置更新到spring容器的Environment中,並且同事bean緩存就會被清空,從而就會從bean工廠中創建bean實例了,而這次創建bean實例的時候就會繼續經歷這個bean的生命周期,使得@Value屬性值能夠從Environment中獲取到最新的屬性值,這樣整個過程就達到了動態刷新配置的效果。

Sentinel

在這裡插入圖片描述

在Sentinel的世界中,萬物都是可以被保護的“資源”,當一個外部請求想要訪問Sentinel的資源時,便會創建一個Entry對象,經過Slot鏈路的層層考驗最終完成自己的業務,可以把Slot當成是一類完成特定任務的“Filter”, 這是一種典型的職責鏈設計模式。

在這些Slot中,有幾個是被專門用來 收集數據 的。比如:

NodeSelectorSlot 被用來構建當前請求的訪問路徑,它將上下游調用鏈串聯起來,形成了一個服務調用關係的樹狀結構。

ClusterBuilderSlotStatisticSlot 這兩個Slot會從多個維度統計一些運行期信息,比如介面響應時間、服務QPS、當前線程數等等。

由這幾個Slot統計出來的結果,會為後續的限流降級等Sentinel策略提供數據支持。

Sentinel還有很多被用作“規則判斷”的Slot。比如:

FlowSlot 被用來做流控規則的判定, DegradeSlot 被用來做降級熔斷判定,這兩個Slot是平時在項目中使用頻率最高的服務容錯功能。

ParamFlowSlot 可以根據請求參數做精細粒度的流控,它經常被用來在大型應用中控制熱點數據所帶來的突發流量。

AuthoritySlot 可以針對特定資源設置黑白名單,限制某些應用對資源的訪問。

除此之外,Sentinel的Slot機制也具備一定的擴展性,如果想要添加一個自定義的Slot,可以通過實現ProcessorSlot介面來完成,而且還可以通過優先順序調整各個Slot之間的執行順序。

運行Sentinel控制台

java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard-1.8.2.jar

將微服務接入到Sentinel控制台

首先,需要把Sentinel的依賴項引入到項目里

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

然後,需要做一些基本的配置

spring:
  cloud:
    sentinel:
      transport:
        # sentinel api埠,預設8719
        port: 8719
        # dashboard地址
        dashboard: localhost:8080

Sentinel會為Controller中的API生成一個預設的資源名稱,這個名稱就是URL的路徑,也可以使用特定的註解為資源打上一個指定的名稱標記。

@SentinelResource(value = "getTemplateInBatch", blockHandler = "getTemplateInBatch_block")

註解中的blockHandler屬性為當前資源指定了限流後的降級方法,如果當前服務拋出了BlockException,那麼就會轉而執行這段限流方法。

設置流控規則

Sentinel支持三種不同的流控模式,分別是直接流控、關聯流控和鏈路流控。

  • 直接流控:直接作用於當前資源,如果訪問壓力大於某個閾值,後續請求將被直接攔下來;

  • 關聯流控:當關聯資源的訪問量達到某個閾值時,對當前資源進行限流;
    在這裡插入圖片描述

    在“關聯資源”一欄填了getTemplate,寫在這裡的是高優先順序資源的名稱。同時,設置了閾值判斷條件為QPS=1,它的意思是,如果高優先順序資源的訪問頻率達到了每秒一次,那麼低優先順序資源就會被限流。

    關聯限流的閾值判斷是作用於高優先順序資源之上的,但是流控效果是作用於低優先順序資源之上

  • 鏈路流控:當指定鏈路上的訪問量大於某個閾值時,對當前資源進行限流,這裡的“指定鏈路”是細化到API級別的限流維度。

在這裡插入圖片描述

在上面的圖裡,一個服務應用中有/api/edit和/api/add兩個介面,這兩個介面都調用了同一個資源resource-1。如果想只對/api/edit介面流進行限流,那麼就可以將“鏈路流控”應用在resource-1之上,同時指定當前流控規則的“入口資源”是/api/edit。

實現針對調用源的限流

在微服務架構中,一個服務可能被多個服務調用。比如說,Customer服務會調用Template服務的getTemplateInBatch資源,未來可能會研發一個新的服務叫coupon-other-serv,它也會調用相同資源。

如果想為getTemplateInBatch資源設置一個限流規則,並指定其只對來自Customer服務的調用起作用

在這裡插入圖片描述

這個實現過程分為兩步。第一步,要想辦法在服務請求中加上一個特殊標記,告訴Template服務是誰調用;第二步,需要在Sentinel控制台設置流控規則的針對來源。

第一步。首先,將調用源的應用名加入到由OpenFeign組件構造的Request中。可以藉助OpenFeign的RequestInterceptor擴展介面,編寫一個自定義的攔截器,在服務請求發送出去之前,往Request的Header里寫入一個特殊變數,傳遞給下游服務的“來源標記”

@Configuration
public class OpenfeignSentinelInterceptor implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate template) {
        template.header("SentinelSource", "coupon-customer-serv");
    }
}

接下來,需要在Template服務中識別來自上游的標記,並將其加入到Sentinel的鏈路統計中。可以藉助Sentinel提供的RequestOriginParser擴展介面,編寫一個自定義的解析器。

@Component
@Slf4j
public class SentinelOriginParser implements RequestOriginParser {

    @Override
    public String parseOrigin(HttpServletRequest request) {
        log.info("request {}, header={}", request.getParameterMap(), request.getHeaderNames());
        return request.getHeader("SentinelSource");
  }

在方法中,從服務請求的Header中獲取SentinelSource變數的值,作為調用源的name

第二步。在流控規則的編輯頁面,“針對來源”這一欄填上coupon-customer-serv並保存,這樣一來,當前限流規則就只會針對來自Customer服務的請求生效了。

在這裡插入圖片描述

Sentinel的流控效果

快速失敗,Sentinel預設的流控效果,在快速失敗模式下,超過閾值設定的請求將會被立即阻攔住。

Warm Up 則實現了“預熱模式的流控效果”,這種方式可以平緩拉高系統水位,避免突發流量對當前處於低水位的系統的可用性造成破壞。舉個例子,如果設置的系統閾值是QPS=10,預熱時間=5,那麼Sentinel會在這5秒的預熱時間內,將限流閾值從3緩慢拉高到10。為什麼起始閾值是3呢?因為Sentinel內部有一個冷載入因數,它的值是3,在預熱模式下,起始閾值的計算公式是單機閾值/冷載入因數,也就是10/3=3。

排隊等待 模式下,超過閾值的請求不會立即失敗,而是會被放入一個隊列中,排好隊等待被處理。一旦請求在隊列中等待的時間超過了設置的超時時間,那麼請求就會被從隊列中移除。

異常降級方案

使用blockHandler屬性指定降級方法的名稱,只能在服務拋出BlockException的情況下執行降級邏輯。

BlockException這個異常類是Sentinel組件自帶的類,當一個請求被Sentinel規則攔截,這個異常便會被拋出。比如請求被Sentinel流控策略阻攔住,或者請求被熔斷策略阻斷了,這些情況下可以使用SentinelResource註解的blockHandler來指定降級邏輯。對於其它RuntimeException的異常類型它就無能為力了。

使用SentinelResource中的另一個屬性fallback可以指定一段通用的降級邏輯。

需要註意,如果降級方法的方法簽名是BlockException,那麼fallback是無法正常工作的。在註解中同時使用了fallback和blockHandler屬性,如果服務拋出BlockException,則執行blockHandler屬性指定的方法,其他異常就由fallback屬性所對應的降級方法接管。

可以通過SentinelResource註解的fallbackClass屬性指定一個保存降級邏輯的Class。

在控制台添加熔斷策略

Sentinel的熔斷規則有3種,分別是異常比例、異常數和慢調用比例。

異常比例

指定以“ 異常比例”為熔斷開關的判斷邏輯。指定10秒的統計視窗內,如果異常調用的比例超過了60%,並且滿足請求數量>=5,就開啟一段為期5秒的熔斷時間。

“熔斷時長”的時間單位是秒,而“統計視窗”的時間單位是毫秒
在這裡插入圖片描述

Sentinel底層通過一段跨度為10秒的滑動視窗來統計服務調用情況。在這段視窗時間內,前三個服務請求全部失敗,這時失敗率已經達到100%,大大超過了定義的60%的閾值,但是熔斷開關卻沒有打開,這是因為統計視窗的最小請求數還沒有達到設定值5。

之後又有兩個請求被處理,一個成功一個失敗,這時請求個數已經達到了5,失敗率是80%,那麼Sentinel就開啟了一段5秒的熔斷時間。在這段時間內,所有來訪請求都不會得到真實的執行,而是轉而執行降級邏輯。

異常數

異常數”熔斷規則和前面設置的異常比例熔斷規則幾乎一樣,唯一的區別就是“異常數”的判定條件是統計視窗內發生異常的個數。

在這裡插入圖片描述

熔斷器開啟的判定條件是異常數>2

慢調用比例

通常來說,慢調用請求所占比例逐漸增多,這是服務雪崩的前兆。為了將影響範圍縮小,要做的就是 儘早捕捉到慢調用請求的比例變化趨勢,及時通過熔斷規則對服務進行減壓

在這裡插入圖片描述

在10秒的統計視窗內,如果響應時間大於1000ms的請求所占總請求數量的比例超過了0.4,並且請求總數量>=5,此時將觸發Sentinel的熔斷開關,開啟5秒的熔斷視窗。

熔斷開關的狀態轉換

Sentinel的熔斷器會在開啟、關閉和半開這三種邏輯狀態之間來回切換
在這裡插入圖片描述

從圖中可以看出,在第一個統計視窗內熔斷器是處於關閉狀態的,達到熔斷判定條件之後,Sentinel開啟了一段熔斷視窗。在這段視窗時間內,熔斷器是處於開啟狀態的,這時新的服務請求會執行降級邏輯。待熔斷視窗結束,Sentinel會將熔斷器狀態置為“半開”狀態,這是一個介於完全開啟和完全關閉之間的中間態。

在半開狀態下,如果有一個新請求過來,那麼Sentinel會試探性地讓這個請求去執行正常的業務邏輯,如果執行成功,那麼Sentinel將關閉熔斷器並退出熔斷狀態,如果執行失敗,那麼Sentinel將再次開啟一個新的熔斷視窗。

接入 Nacos 實現規則持久化

通過集成Nacos Config來實現持久化方案,需要把Sentinel中設置的限流規則保存到Nacos配置中心。這樣一來,當應用服務或Sentinel Dashboard重新啟動時,它們就可以自動把Nacos中的限流規則同步到本地,不管怎麼重啟服務都不會導致規則失效了。

在這裡插入圖片描述

Sentinel控制台將限流規則同步到了Nacos Config伺服器來實現持久化。同時,在應用程式中,配置了一個Sentinel Datasource,從Nacos Config伺服器獲取具體配置信息。

在應用啟動階段,程式會主動從Sentinel Datasource獲取限流規則配置。而在運行期,也可以在Sentinel控制台動態修改限流規則,應用程式會實時監聽配置中心的數據變化,進而獲取變更後的數據。

Sentinel組件二次開發

需要將Sentinel的代碼下載到本地。可以從 GitHub的Releases頁面 的Assets面板中下載Source code源文件。

將項目導入到開發工具中主要針對其中的sentinel-dashboard子模塊做二次開發。整個改造過程按照先後順序將分為三個步驟:

  1. 修改Nacos依賴項的應用範圍,將其打入jar包中;
  2. 後端程式對接Nacos,將Sentinel限流規則同步到Nacos;
  3. 開放單獨的前端限流規則配置頁面。

修改Nacos依賴項

sentinel-dashboard項目的pom.xml文件中的依賴項sentinel-datasource-nacos是連接Nacos Config所依賴的必要組件。需要將這個依賴項的scope標簽註釋掉。

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    <!-- 將scope註釋掉,改為編譯期打包 -->
    <!--<scope>test</scope>-->
</dependency>

後端程式對接Nacos

打開sentinel-dashboard項目下的src/test/java目錄(註意是test目錄而不是main目錄),然後定位到com.alibaba.csp.sentinel.dashboard.rule.nacos包。在這個包下麵,看到4個和Nacos Config有關的類,它們的功能描述如下:

  • NacosConfig:初始化Nacos Config的連接;
  • NacosConfigUtil:約定了Nacos配置文件所屬的Group和文件命名尾碼等常量欄位;
  • FlowRuleNacosProvider:從Nacos Config上獲取限流規則;
  • FlowRuleNacosPublisher:將限流規則發佈到Nacos Config。

為了讓這些類在Sentinel運行期可以發揮作用,需要在src/main/java下創建同樣的包路徑,然後將這四個文件從test路徑拷貝到main路徑下。

NacosConfig類中配置Nacos連接串

打開NacosConfig類,找到其中的nacosConfigService方法。這個方法創建了一個ConfigService類,它是Nacos Config定義的通用介面,提供了Nacos配置項的讀取和更新功能。FlowRuleNacosProvider和FlowRuleNacosPublisher這兩個類都是基於這個ConfigService類實現Nacos數據同步的。改造後的代碼:

@Bean
public ConfigService nacosConfigService() throws Exception {
    // 將Nacos的註冊地址引入進來
    //也可以通過配置文件來註入serverAddr和namespace等屬性。
    Properties properties = new Properties();
    properties.setProperty("serverAddr", "localhost:8848");
    properties.setProperty("namespace", "dev");

    return ConfigFactory.createConfigService(properties);
}

在Controller層接入Nacos來實現限流規則持久化

在FlowControllerV2中正式接入Nacos。FlowControllerV2對外暴露了REST API,用來創建和修改限流規則。在這個類的源代碼中,需要修改兩個變數的Qualifier註解值。

@Autowired
// 指向剛纔從test包中遷移過來的FlowRuleNacosProvider類
@Qualifier("flowRuleNacosProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;

@Autowired
// 指向剛纔從test包中遷移過來的FlowRuleNacosPublisher類
@Qualifier("flowRuleNacosPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;

通過Qualifier標簽將FlowRuleNacosProvider註入到了ruleProvier變數中,又採用同樣的方式將FlowRuleNacosPublisher註入到了rulePublisher變數中。FlowRuleNacosProvider和FlowRuleNacosPublisher就是從test目錄Copy到main目錄下的兩個類。

修改完成之後,FlowControllerV2底層的限流規則改動就會被同步到Nacos伺服器了。這個同步工作是由FlowRuleNacosPublisher執行的,它會發送一個POST請求到Nacos伺服器來修改配置項。

FlowRuleNacosPublisher會在Nacos Config上創建一個用來保存限流規則的配置文件,這個配置文件以“application.name”開頭,以“-flow-rules”結尾,而且它所屬的Group為“SENTINEL_GROUP”。這裡用到的文件命名規則和Group都是通過NacosConfigUtil類中的常量指定的。

前端頁面改造

打開sentinel-dashboard模塊下的webapp目錄,該目錄存放了Sentinel控制台的前端頁面資源。需要改造的文件是sidebar.html,這個html文件定義了控制台的左側導航欄。

<li ui-sref-active="active">
  <a ui-sref="dashboard.flow({app: entry.app})">
    <i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控規則持久化</a>
</li>

微服務改造

只需要添加一個新的sentinel-datasource-nacos依賴項,併在配置文件中添加sentinel datasource連接信息就可以了

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
spring:
 cloud:
  sentinel:
    datasource:
      # 數據源的key,可以自由命名
      geekbang-flow:
        # 指定當前數據源是nacos
        nacos:
          # 設置Nacos的連接地址、命名空間和Group ID
          server-addr: localhost:8848
          namespace: dev
          groupId: SENTINEL_GROUP
          # 設置Nacos中配置文件的命名規則
          dataId: ${spring.application.name}-flow-rules
          # 必填的重要欄位,指定當前規則類型是"限流"
          rule-type: flow

在微服務端的sentinal數據源中配置的namespace和groupID,一定要和Sentinal Dashoboard二次改造中的中的配置相同,否則將無法正常同步限流規則。Sentinal Dashboard中namespace是在NacosConfig類中指定的,而groupID是在NacosConfigUtil類中指定的。

dataId的文件命名規則,需要和Sentinel二次改造中的FlowRuleNacosPublisher類保持一致,如果修改了FlowRuleNacosPublisher中的命名規則,那麼也要在每個微服務端做相應的變更。

調用鏈追蹤:集成 Sleuth 和 Zipkin

Sleuth

如果想提高線上異常排查的效率,那麼首先要做的一件事就是: 將一次調用請求中所有訪問到的微服務日誌前後串聯起來

鏈路追蹤技術會為每次服務調用生成一個全局唯一的ID(Trace ID),從本次服務調用的起點到終點,這個過程中的所有日誌信息都會被打上Trace ID的烙印。這樣一來,根據日誌中的Trace ID,就能很清晰地梳理出一次服務請求前後都經過了哪些微服務節點。

Sleuth的底層邏輯

調用鏈追蹤有兩個任務,一是 標記出一次調用請求中的所有日誌,二是 梳理日誌間的前後關係

集成了Sleuth組件之後,它會嚮日志中打入三個“特殊標記”,其中一個標記是Trace ID。剩下的兩個標記分別是Span ID和Parent Span ID,這倆用來表示調用的前後順序關係。

Trace ID完成的是第一個任務:標記,用來標記調用鏈的全局唯一ID。

Span是Sleuth下麵的一個基本工作單元,當服務請求抵達當前單元時,Sleuth就會為這個單元分配一個獨一無二的Span ID,並標記單元的開始時間和結束時間,這樣就可以記錄每個單元的處理用時了。

Parent Span ID指向了當前單元的父級單元,也就是上游的調用者。一個環環相扣的調用鏈就通過Parent Span ID被串了起來。

在這裡插入圖片描述

上面的圖示只是一個簡化的流程,在實際的項目中,一次服務調用可不光只會生成一個Span。比如說服務A請求通過OpenFeign組件調用了服務B,那麼服務A接收用戶請求的過程就是一個單元,而OpenFeign組件發起遠程調用的過程又是另一個單元。由此可見,單元的顆粒度其實是非常小的。

Sleuth還有一個特殊的數據結構,叫做Annotation,被用來記錄一個具體的“事件”。

在這裡插入圖片描述

集成Sleuth實現鏈路打標

將Sleuth的依賴項添加到pom.xml文件中

<!-- Sleuth依賴項 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>

application.yml配置文件

spring:
  sleuth:
    sampler:
      # 採樣率的概率,100%採樣
      probability: 1.0
      # 每秒採樣數字最高為1000
      rate: 1000

probability,是一個0到1的浮點數,用來表示 採樣率。這裡設置的probability是1,就表示對請求進行100%採樣。如果把probability設置成小於1的數,就說明有的請求不會被採樣。如果一個請求未被採樣,那麼它將不會被調用鏈追蹤系統Track起來。

rate參數,它代表 每秒最多可以對多少個Request進行採樣。這有點像一個“限流”參數,如果超過這個閾值,服務請求仍然會被正常處理,但調用鏈信息不會被採樣。

Sleuth如何在調用鏈中傳遞標記

Sleuth為了將Trace ID和調用方服務的Span ID傳遞給被調用的微服務,它在OpenFeign的環節動了一個手腳。Sleuth通過 TracingFeignClient類,將一系列Tag標記塞進了OpenFeign構造的服務請求的Header結構中。

在TracingFeignClient的類中打了一個Debug斷點,將Request的Header信息列印出來:
在這裡插入圖片描述

在這個Header結構中,可以看到有幾個以X-B3開頭的特殊標記,這個X-B3就是Sleuth的特殊接頭暗號。其中X-B3-TraceId就是全局唯一的鏈路追蹤ID,而X-B3-SpanId和X-B3-ParentSpandID分別是當前請求的單元ID和父級單元ID,最後的X-B3-Sampled則表示當前鏈路是否是一個已被採樣的鏈路。通過Header里的這些信息,下游服務就完整地得到了上游服務的情報。

使用Zipkin收集並查看鏈路數據

Zipkin是一個分散式的Tracing系統,它可以用來收集時序化的鏈路打標數據。通過Zipkin內置的UI界面,可以根據Trace ID搜索出一次調用鏈所經過的所有訪問單元,並獲取每個單元在當前服務調用中所花費的時間。

為了搭建一條高可用的鏈路信息傳遞通道,使用RabbitMQ作為中轉站,讓各個應用伺服器將服務調用鏈信息傳遞給RabbitMQ,而Zipkin伺服器則通過監聽RabbitMQ的隊列來獲取調用鏈數據。相比於讓微服務通過Web介面直連Zipkin, 使用消息隊列可以大幅提高信息的送達率和傳遞效率

搭建Zipkin伺服器

通過訪問 maven的中央倉庫 下載zipkin-server-2.23.9-exec.jar文件

java -jar zipkin-server-2.23.9-exec.jar --zipkin.collector.rabbitmq.addresses=localhost:5672

Zipkin已經內置了RabbitMQ的預設連接屬性,如果沒有特殊指定,那麼Zipkin會使用guest預設用戶登錄RabbitMQ。

在這裡插入圖片描述

搭建Zipkin有兩種方式,一種是直接下載Jar包,這是官方推薦的標準集成方式;另一種是通過引入Zipkin依賴項的方式,在本地搭建一個Spring Boot版的Zipkin伺服器。如果需要對Zipkin做定製化開發,那麼可以採取後一種方式。

傳送鏈路數據到Zipkin

在每個微服務模塊的pom.xml中添加Zipkin適配插件和Stream的依賴

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-sleuth-zipkin</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>

將Zipkin的配置信息添加到每個微服務模塊的application.yml文件中。Zipkin適配器還支持ActiveMQ、Kafka和直連的方式

spring:
  zipkin:
    sender:
      type: rabbit
    rabbitmq:
      addresses: 127.0.0.1:5672
      queue: zipkin #在應用中指定的隊列名稱,一定要同Zipkin伺服器所指定的隊列名稱保持一致

在瀏覽器中打開localhost:9411進到Zipkin的首頁,在首頁中可以通過各種搜索條件的組合,從服務、時間等不同維度查詢調用鏈數據。

Spring Cloud Gateway

Gateway叫“微服務網關”,就說明它自己就是一個微服務。換句話說,它也是Nacos服務註冊中心的一員。既然Gateway能連接到Nacos,那麼就意味著它可以輕鬆獲取到Nacos中所有服務的註冊表。這樣一來,Gateway就可以根據本地的路由規則,將請求精準無誤地送達到每個微服務組件中。

高可擴展性,對後臺的微服務集群做擴容或縮容的時候,Gateway可以從Nacos註冊中心輕鬆獲取所有服務節點的變動,不需要任何額外的配置,一切都在無感知的情況下自然而然地發生。

高度可定製化,它提供了一種對開發人員非常友好的方式,可以通過Java代碼去定製各種複雜的路由邏輯,還可以使用Filter對請求進行加工。

Gateway路由規則

Gateway的路由規則主要有三個部分,分別是路由、謂詞和過濾器。

在這裡插入圖片描述

路由

路由是Gateway的一個基本單元,每個路由都有一個目標地址,這個目標地址就是當前路由規則要調用的目標服務。那麼一條路由規則在什麼情況下會去調用目標服務呢?這就要看路由的謂詞設置了。

謂詞

所謂謂詞,實際上是路由的判斷規則,一個路由中可以添加多個謂詞的組合。如果一個服務請求滿足某個路由里設置的所有的謂詞規則,那麼就說明這個請求是當前路由的心動女神,這時候Gateway就會把請求轉發到路由中設置的目標地址。

過濾器

過濾器和路由、目標地址之間是什麼關係呢?其實Gateway在把請求轉發給目標地址的過程中,把這個任務全權委托給了Filter(過濾器)來處理。

在這裡插入圖片描述

Gateway組件使用了一種FilterChain的模式對請求進行處理,每一個服務請求(Request)在發送到目標服務之前都要被一串FilterChain處理。同理,在Gateway接收服務響應(Response)的過程中也會被FilterChain處理一把。

Gateway的過濾器主要分為兩種,一種是GlobalFilter,也就是“ 全局過濾器”;另一種是GatewayFilter,也就是對指定路由生效的“ 局部過濾器”。

全局過濾器繼承自GlobalFilter介面,它的作用大多是“例行公事”,也就是一些底層能力的支持。比如,RouteToRequestUrlFilter這個全局過

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • CSS中的BFC、IFC、GFC和FFC是佈局模型中的概念,用於描述元素在文檔流中的佈局行為。它們分別代表塊級格式化上下文(Block Formatting Context)、內聯級格式化上下文(Inline Formatting Context)、網格格式化上下文(Grid Formatting ...
  • tips:如果本文對你有用,請愛心點個贊,提高排名,讓這篇文章幫助更多的人。謝謝大家!比心❤~ 如果解決不了,可以在文末加我微信,進群交流一起學習探討。 背景 項目的要求需要實現規範化,針對項目內所有的滾動條做優化,需要按照UE調整實現:滾動時出現滾動條,停留三秒後,自動消失 由於是就項目的優化,所 ...
  • 我們是袋鼠雲數棧 UED 團隊,致力於打造優秀的一站式數據中台產品。我們始終保持工匠精神,探索前端道路,為社區積累並傳播經驗價值。 前言 數棧作為雲原⽣⼀站式⼤數據開發平臺,從2016年發佈第⼀個版本開始,數棧就始終堅持著以技術為 核⼼、安全為底線、提效為⽬標、中台為戰略的思想,堅定不移地⾛國產化信 ...
  • 本文是系列第四篇。系列文章: 現代圖片性能優化及體驗優化指南 - 圖片類型及 Picture 標簽的使用 現代圖片性能優化及體驗優化指南 - 響應式圖片方案 現代圖片性能優化及體驗優化指南 - # 縮放精細化展示及避免佈局偏移、拉伸 圖片資源,在我們的業務中可謂是占據了非常大頭的一環,尤其是其對帶寬 ...
  • cola前言 COLA提供了一整套代碼架構,拿來即用。 其中包含了很多架構設計思想,包括討論度很高的領域驅動設計DDD等。 COLA 的分層是一種經過改良的三層架構,主要是講傳統的業務邏輯層拆分為展示層、應用層、領域層和基礎設施層。 展示層(Presentation Layer):負責以 Rest ...
  • 1.簡介 定義:將某個對象中圍繞某個主題的一些列行為委托給一個代理對象去執行,代理對象將控制和管理對原有對象的訪問,調用者想要訪問目標對象,必須通過代理對象去間接訪問,代理對象在調用方和目標對象之間可以起到”中介“的作用。代理一詞本身,其實就可以很好發現的關鍵點,如果暫時無法理解晦澀的概念,那麼在閱 ...
  • *以下內容為本人的學習筆記,如需要轉載,請聲明原文鏈接 微信公眾號「englyf」https://mp.weixin.qq.com/s/y-npGelPJwmx3iNvHaXRTg 本文上接《Python:Excel自動化實踐入門篇 甲》 正文開始之前,提醒一下朋友們,送圖書的活動還在繼續,朋友們請 ...
  • Spring Boot 支持 Java Util Logging,Log4J,Log4J2 和 Logback 等日誌框架,預設採用 Logback 日誌。 在實際 Spring Boot 項目中使用 Spring Boot 預設日誌配置是不能夠滿足實際生產及開發需求的,需要選定適合的日誌輸出框架, ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...