天天看點

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

Spring Cloud 微服務講義

  • 第一部分 微服務架構
    • 第 1 節 網際網路應用架構演進
    • 第 2 節 微服務架構展現的思想及優缺點
    • 第 3 節 微服務架構中的核心概念
  • 第二部分 Spring Cloud 綜述
    • 第 1 節 Spring Cloud 是什麼
    • 第 2 節 Spring Cloud 解決什麼問題
    • 第 3 節 Spring Cloud 架構
      • 3.1 Spring Cloud 核心元件
      • 3.2 Spring Cloud 體系結構(元件協同工作機制)
    • 第 4 節 Spring Cloud 與 Dubbo 對比
    • 第 5 節 Spring Cloud 與 Spring Boot 的關系
  • 第三部分 案例準備
    • 第 1 節 案例說明
    • 第 2 節 案例資料庫環境準備
    • 第 3 節 案例工程
      • 3.1 父工程 hyq-parent
      • 3.2 公共元件微服務
      • 3.3 商品微服務
      • 3.4 頁面靜态化微服務
    • 第 4 節 案例代碼問題分析
  • 第四部分 第一代 Spring Cloud 核心元件
    • 第 1 節 Eureka服務注冊中心
      • 1.1 關于服務注冊中心
        • 1.1.1注冊中心實作原理
        • 1.1.2主流服務中心對比
      • 1.2 服務注冊中心元件 Eureka
      • 1.3 搭建單例Eureka Server服務注冊中心
        • 1.3.1、搭建Eureka Server服務 hyq-cloud-eureka
        • 1.3.2、hyq-cloud-eureka工程pom.xml中引入依賴
        • 1.3.3、在yml檔案中配置Eureka server服務端口,服務名等資訊
        • 1.3.4、編寫啟動類,聲明目前服務為Eureka注冊中心
        • 1.3.5、通路http://127.0.0.1:9200
        • 1.3.6、商品微服務和頁面靜态化微服務注冊到Eureka
      • 1.4 搭建Eureka Server 高可用叢集
      • 1.5 Eureka細節詳解
        • 1.5.1 Eureka中繼資料詳解
        • 1.5.2 Eureka用戶端詳解
        • 1.5.3 Eureka服務端詳解
    • 第 2 節 Ribbon負載均衡
      • 2.1 關于負載均衡
      • 2.2 Ribbon進階應用
      • 2.3 Ribbon負載均衡政策
      • 2.4 Ribbon核心源碼剖析
    • 第 3 節Hystrix熔斷器
      • 3.1 微服務中的雪崩效應
      • 3.2 雪崩效應解決方案
      • 3.3 Hystrix簡介
      • 3.4 Hystrix應用
        • 3.4.1.熔斷處理
        • 3.4.2降級處理
      • 3.5 Hystrix艙壁模式
      • 3.6 Hystrix工作流程與進階應用
    • 第 4 節 Feign遠端調用元件
      • 4.1 Feign簡介
      • 4.2 Feign配置應用
      • 4.3 Feign對負載均衡的支援
      • 4.4 Feign對熔斷器的支援
      • 4.5 Feign對請求壓縮和響應壓縮的支援
    • 第 5 節 GateWay網關元件
      • 5.1 GateWay簡介
      • 5.2 GateWay核心概念
      • 5.3 GateWay如何工作
      • 5.4 GateWay應用
      • 5.5 GateWay路由規則詳解
      • 5.6 GateWay動态路由詳解
      • 5.7 GateWay過濾器
        • 5.7.1 GateWay過濾器簡介
        • 5.7.2 自定義全局過濾器實作IP通路限制(黑白名單)
      • 5.8 GateWay高可用
    • 第 6 節 Spring Cloud Config 分布式配置中心
      • 6.1 分布式配置中心應用場景
      • 6.2 Spring Cloud Config
        • 6.2.1 Config簡介
        • 6.2.2 Config分布式配置應用
      • 6.3 Config配置手動重新整理
      • 6.4 Config配置自動更新
        • 6.4.1 消息總線Bus
        • 6.4.2 Spring Cloud Config + Spring Cloud Bus 實作自動重新整理

第一部分 微服務架構

第 1 節 網際網路應用架構演進

随着互聯⽹的發展,⽤戶群體逐漸擴大,⽹站的流量成倍增⻓,正常的單體架構已⽆法滿⾜請求壓⼒和業務的快速疊代,架構的變化勢在必⾏。下⾯我們就最開始的單體架構分析,⼀步步的到現在的微服務架構。

淘寶:LAMP,Linux、Apache、MySQL、PHP

  • 1)單體應用架構

    在誕⽣之初,一般項目的⽤戶量、資料量規模都⽐較⼩,項目所有的功能子產品都放在一個工程中編碼、

    編譯、打包并且部署在一個Tomcat容器中的架構模式就是單體應用架構,這樣的架構既簡單實 ⽤、便

    于維護,成本⼜低,成為了那個時代的主流架構⽅式。

    Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件
    Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件
    優點:
    高效開發:項⽬前期開發節奏快,團隊成員少的時候能夠快速疊代
    架構簡單:MVC架構,隻需要借助IDE開發、調試即可
    易于測試:隻需要通過單元測試或者浏覽器完成
    易于部署:打包成單⼀可執⾏的jar或者打成war包放到容器内啟動單體架構的應用比較容易部署、測試, 在項目的初期,
    		單體應用可以很好地運作。然而,随着需求的不斷增加, 越來越多的人加入開發團隊,代碼庫也在飛速地膨脹。
    		慢慢地,單體應用變得越來越臃腫,可維護性、靈活性逐漸降低,維護成本越來越高。
               
    缺點:
    可靠性差: 某個應用Bug,例如死循環、記憶體溢出等, 可能會導緻整個應用的崩潰
    複雜性高: 以一個百萬行級别的單體應用為例,整個項目包含的子產品多、子產品的邊界模糊、 依賴關系不清晰、 
    		代碼品質參差不齊、 混亂地堆砌在一起。使得整個項目非常複雜。
    擴充能力受限: 單體應用隻能作為一個整體進行擴充,無法根據業務子產品的需要進行伸縮。例如,
    		應用中有的子產品是計算密集型的,它需要強勁的CPU; 有的子產品則是IO密集型的,需要更大的記憶體。 
    		由于這些子產品部署在一起,不得不在硬體的選擇上做出妥協。
               

    業務量上漲之後,單體應用架構進一步豐富變化,比如應用叢集部署、使用Nginx進行負載均衡、增加

    緩存伺服器、增加檔案伺服器、資料庫叢集并做讀寫分離等,通過以上措施增強應對高并發的能力、應

    對一定的複雜業務場景,但依然屬于單體應用架構。

    Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件
  • 2)垂直應用架構

    為了避免上⾯提到的那些問題,開始做子產品的垂直劃分,做垂直劃分的原則是基于項目現有的業務

    特性來做,核心目标标第⼀個是為了業務之間互不影響,第⼆個是在研發團隊的壯⼤後為了提⾼效率,

    減少元件之間的依賴。

    Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件
    優點
    系統拆分實作了流量分擔,解決了并發問題
    
    可以針對不同子產品進⾏優化
    
    ⽅便⽔平擴充,負載均衡,容錯率提⾼
    
    系統間互相獨⽴,互不影響,新的業務疊代時更加⾼效
               
    缺點
    服務之間互相調⽤,如果某個服務的端⼝或者ip位址發⽣改變,調⽤的系統得⼿動改變
    
    搭建叢集之後,實作負載均衡⽐較複雜,如:内⽹負載,在遷移機器時會影響調⽤⽅的路 由,導緻
    線上故障
    
    服務之間調⽤⽅式不統⼀,基于 httpclient 、 webservice ,接⼝協定不統⼀
    
    服務監控不到位:除了依靠端⼝、程序的監控,調⽤的成功率、失敗率、總耗時等等這些監 控名額
    是沒有的
               
  • 3)SOA應用架構

    在做了垂直劃分以後,子產品随之增多,維護的成本在也變⾼,⼀些通⽤的業務和子產品重複的越來越多,為了解決上⾯提到的接⼝協定不統⼀、服務⽆法監控、服務的負載均衡,引⼊了阿⾥巴巴開源的Dubbo ,⼀款⾼性能、輕量級的開源Java RPC架構,可以和Spring架構無縫內建。它提供了三⼤核⼼能⼒:⾯向接⼝的遠端⽅法調⽤,智能容錯和負載均衡,以及服務⾃動注冊和發現。

    SOA (Service-Oriented Architecture),即面向服務的架構。根據實際業務,把系統拆分成合适的、獨立部署的子產品,子產品之間互相獨立(通過Webservice/Dubbo等技術進行通信)。

    優點:分布式、松耦合、擴充靈活、可重用。

    缺點:服務抽取粒度較大、服務調用方和提供方耦合度較高(接口耦合度)

    Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件
  • 4)微服務應用架構

    微服務架構可以說是SOA架構的一種拓展,這種架構模式下它拆分粒度更小、服務更獨立。把應用

    拆分成為一個個微小的服務,不同的服務可以使用不同的開發語言和存儲,服務之間往往通過Restful等

    輕量級通信。微服務架構關鍵在于微小、獨立、輕量級通信。

    微服務是在 SOA 上做的升華粒度更加細緻,微服務架構強調的⼀個重點是業務需要徹底的元件化和

    服務化

    Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

    微服務架構和SOA架構相似又不同

    微服務架構和SOA架構很明顯的一個差別就是服務拆分粒度的不同,但是對于項目的架構發展來說,我們所看到的SOA階段其實服務拆分粒度相對來說已經比較細了(超前哦!),是以上述項目SOA到項目微服務,從服務拆分上來說變化并不大,隻是引入了相對完整的新一代Spring Cloud微服務技術。自然,上述我們看到的都是項目架構演變的階段結果,每一個階段其實都經曆了很多變化,項目的服務拆分其實也是走過了從粗到細,并非絕對的一步到位。

    舉個案例來說明SOA和微服務拆分粒度不同

    我們在SOA架構的初期,“履歷投遞子產品”和“人才搜尋子產品”都有履歷内容展示的需求,隻不過說可能

    略有差別,一開始在兩個子產品中各維護了一套履歷查詢和展示的代碼;後期我們将服務更細粒度拆分,

    拆分出履歷基礎服務,那麼不同子產品調用這個基礎服務即可。

第 2 節 微服務架構展現的思想及優缺點

微服務架構設計的核心思想就是**“微”**,拆分的粒度相對比較小,這樣的話單一職責、開發的耦合度

就會降低、微小的功能可以獨立部署擴充、靈活性強,更新改造影響範圍小。

微服務架構的優點:

  • 微服務很小,便于特定業務功能的聚焦
  • 微服務很小,每個微服務都可以被一個小團隊單獨實施(開發、測試、部署上線、運維),團隊合

    作一定程度解耦,便于實施靈活開發

  • 微服務很小,便于重用和子產品之間的組裝
  • 微服務很獨立,那麼不同的微服務可以使用不同的語言開發,松耦合
  • 微服務架構下,我們更容易引入新技術

微服務架構的缺點

  • 微服務架構下,分布式複雜難以管理,當服務數量增加,管理将越加複雜;
  • 微服務架構下,分布式鍊路跟蹤難等;

第 3 節 微服務架構中的核心概念

  • 服務注冊與服務發現
    例如:職位搜尋 ->履歷服務
    服務提供者:履歷服務
    服務消費者:職位搜尋
    服務注冊:服務提供者将所提供服務的資訊(伺服器IP和端口、服務通路協定等)注冊/登記到注冊中心
    服務發現:服務消費者能夠從注冊中心擷取到較為實時的服務清單,然後根據一定的政策選擇一個服務通路
               
    Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件
  • 負載均衡
    負載均衡即将請求壓力配置設定到多個伺服器(應用伺服器、資料庫伺服器等),以此來提高服務的性能、可靠性
               
    Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件
  • 熔斷
    熔斷即斷路保護。微服務架構中,如果下遊服務因通路壓力過大而響應變慢或失敗,上遊服務為了保護系統整體可用性,
    可以暫時切斷對下遊服務的調用。這種犧牲局部,保全整體的措施就叫做熔斷。
               
    Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件
  • 鍊路追蹤
    微服務架構越發流行,一個項目往往拆分成很多個服務,那麼一次請求就需要涉及到很多個服務。不同的微服務可能是由不同的團隊開發、
    可能使用不同的程式設計語言實作、整個項目也有可能部署在了很多伺服器上(甚至百台、千台)橫跨多個不同的資料中心。所謂鍊路追蹤,
    就是對一次請求涉及的很多個服務鍊路進行日志記錄、性能監控
               
    Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件
  • API 網關
微服務架構下,不同的微服務往往會有不同的通路位址,用戶端可能需要調用多個服務的接口才能完成一個業務需求,
如果讓用戶端直接與各個微服務通信可能出現:
	1)用戶端需要調用不同的url位址,增加了維護調用難度
	2)在一定的場景下,也存在跨域請求的問題(前後端分離就會碰到跨域問題,原本我們在後端采用Cors就能解決,
		現在利用網關,那麼就放在網關這層做好了)
	3)每個微服務都需要進行單獨的身份認證
	
那麼,API網關就可以較好的統一處理上述問題,API請求調用統一接入API網關層,由網關轉發請
求。API網關更專注在安全、路由、流量等問題的處理上(微服務團隊專注于處理業務邏輯即可),它的
功能比如
	1)統一接入(路由)
	2)安全防護(統一鑒權,負責網關通路身份認證驗證,與“通路認證中心”通信,實際認證業務邏輯交移“通路認證中心”處理)
	3)黑白名單(實作通過IP位址控制禁止通路網關功能,控制通路)
	3)協定适配(實作通信協定校驗、适配轉換的功能)
	4)流量管控(限流)
	5)長短連結支援
	6)容錯能力(負載均衡)
           
Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

第二部分 Spring Cloud 綜述

第 1 節 Spring Cloud 是什麼

[百度百科]Spring Cloud是一系列架構的有序集合。它利用Spring Boot的開發便利性巧妙地簡化了分

布式系統基礎設施的開發,如服務發現注冊、配置中心、消息總線、負載均衡、斷路器、資料監控等,

都可以用 Spring Boot的開發風格做到一鍵啟動和部署。Spring Cloud并沒有重複制造輪子,它隻是将

目前各家公司開發的比較成熟、經得起實際考驗的服務架構組合起來,通過Spring Boot風格進行再封

裝屏蔽掉了複雜的配置和實作原理,最終給開發者留出了一套簡單易懂、易部署和易維護的分布式系統

開發工具包。

Spring Cloud是一系列架構的有序集合(Spring Cloud是一個規範) ,開發服務發現注冊、配置中心、消息總線、負載均衡、斷路器、資料監控等,利用Spring Boot的開發便利性簡化了微服務架構的開發(自動裝配)

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

這裡,我們需要注意,Spring Cloud其實是一套規範,是一套用于建構微服務架構的規範,而不是一個可以拿來即用的架構(所謂規範就是應該有哪些功能元件,然後元件之間怎麼配合,共同完成什麼事情)。在這個規範之下第三方的Netflix公司開發了一些元件、Spring官方開發了一些架構/元件,包括第三方的阿裡巴巴開發了一套架構/元件集合Spring Cloud Alibaba,這些才是Spring Cloud規範的實作。

Netflix搞了一套 ,簡稱SCN

Spring Cloud 吸收了Netflix公司的産品基礎之上自己也搞了幾個元件

阿裡巴巴在之前的基礎上搞出了一堆微服務元件,Spring Cloud Alibaba(SCA)

第 2 節 Spring Cloud 解決什麼問題

Spring Cloud 規範及實作意圖要解決的問題其實就是微服務架構實施過程中存在的一些問題,比如微服務架構中的服務注冊發現問題、網絡問題(比如熔斷場景)、統一認證安全授權問題、負載均衡問題、鍊路追蹤等問題。

  • Distributed/versioned configuration (分布式/版本化配置)
  • Service registration and discovery (服務注冊和發現)
  • Routing (智能路由)
  • Service-to-service calls (服務調用)
  • Load balancing (負載均衡)
  • Circuit Breakers (熔斷器)
  • Global locks (全局鎖)
  • Leadership election and cluster state ( 選舉與叢集狀态管理)
  • Distributed messaging (分布式消息傳遞平台)

第 3 節 Spring Cloud 架構

如之前所述,Spring Cloud是一個微服務相關規範,這個規範意圖為搭建微服務架構提供一站式服務,采用元件(架構)化機制定義一系列元件,各類元件針對性的處理微服務中的特定問題,這些元件共同來構成Spring Cloud微服務技術棧

3.1 Spring Cloud 核心元件

Spring Cloud 生态圈中的元件,按照發展可以分為第一代 Spring Cloud元件和第二代 Spring Cloud元件。

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

3.2 Spring Cloud 體系結構(元件協同工作機制)

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

Spring Cloud中的各元件協同工作,才能夠支援一個完整的微服務架構。比如

  • 注冊中心負責服務的注冊與發現,很好将各服務連接配接起來
  • API網關負責轉發所有外來的請求
  • 斷路器負責監控服務之間的調用情況,連續多次失敗進行熔斷保護。
  • 配置中心提供了統一的配置資訊管理服務,可以實時的通知各個服務擷取最新的配置資訊

第 4 節 Spring Cloud 與 Dubbo 對比

Dubbo是阿裡巴巴公司開源的一個高性能優秀的服務架構,基于RPC調用,對于目前使用率較高的Spring Cloud Netflix來說,它是基于HTTP的,是以效率上沒有Dubbo高,但問題在于Dubbo體系的元件不全,不能夠提供一站式解決方案,比如服務注冊與發現需要借助于Zookeeper等實作,而Spring Cloud Netflix則是真正的提供了一站式服務化解決方案,且有Spring大家族背景。

前些年,Dubbo使用率高于SpringCloud,但目前Spring Cloud在服務化/微服務解決方案中已經有了非常好的發展趨勢。

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

第 5 節 Spring Cloud 與 Spring Boot 的關系

Spring Cloud 隻是利用了Spring Boot 的特點,讓我們能夠快速的實作微服務元件開發,否則不使用Spring Boot的話,我們在使用Spring Cloud時,每一個元件的相關Jar包都需要我們自己導入配置以及需要開發人員考慮相容性等各種情況。是以Spring Boot是我們快速把Spring Cloud微服務技術應用起來的一種方式。

第三部分 案例準備

第 1 節 案例說明

本部分我們按照普通方式模拟一個微服務之間的調用,後續我們将一步步使用Spring Cloud的元件對案例進行改造。

需求:

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

完整業務流程圖:

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

第 2 節 案例資料庫環境準備

本教程資料庫使用Mysql 5.7.x

商品資訊表:

create table products(
	id INT PRIMARY KEY AUTO_INCREMENT,
	NAME VARCHAR(50), #商品名稱
	price DOUBLE,
	flag VARCHAR(2), #上架狀态
	goods_desc VARCHAR(100), #商品描述
	images VARCHAR(400), #商品圖檔
	goods_stock INT, #商品庫存
	goods_type VARCHAR(20) #商品類型
);
           

第 3 節 案例工程

我們基于SpringBoot來構造工程環境,我們的工程子產品關系如下所示:

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

3.1 父工程 hyq-parent

在Idea中建立project,命名為hyq-parent

pom.xml

<?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">
    <modelVersion>4.0.0</modelVersion>

    <!--spring boot 父啟動器依賴-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>

    <groupId>com.hyq</groupId>
    <artifactId>hyq-parent</artifactId>
    <version>1.0-SNAPSHOT</version>
    <!--父工程打包方式-->
    <packaging>pom</packaging>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <dependencies>
        <!--web依賴-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--日志依賴-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </dependency>
        <!--測試依賴-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--lombok工具-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
            <scope>provided</scope>
        </dependency>
        <!-- Actuator可以幫助你監控和管理Spring Boot應用-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--熱部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
    </dependencies>


    <build>
        <plugins>
            <!--編譯插件-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                    <encoding>utf-8</encoding>
                </configuration>
            </plugin>
            <!--打包插件-->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>
           

3.2 公共元件微服務

  1. 在公共元件微服務中引入資料庫驅動及mybatis-plus
    <?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>hyq-parent</artifactId>
            <groupId>com.hyq</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>hyq-service-common</artifactId>
    
        <properties>
            <maven.compiler.source>11</maven.compiler.source>
            <maven.compiler.target>11</maven.compiler.target>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>com.baomidou</groupId>
                <artifactId>mybatis-plus-boot-starter</artifactId>
                <version>3.3.2</version>
            </dependency>
            <!--pojo持久化使用-->
            <dependency>
                <groupId>javax.persistence</groupId>
                <artifactId>javax.persistence-api</artifactId>
                <version>2.2</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.37</version>
                <scope>runtime</scope>
            </dependency>
        </dependencies>
    </project>
               
  2. 生成資料庫實體類:com.hyq.common.pojo.Products
    package com.hyq.common.pojo;
    
    import lombok.Data;
    
    import javax.persistence.Id;
    import javax.persistence.Table;
    
    @Data
    @Table(name = "products")
    public class Products {
        @Id
        private Integer id;
        private String name;
        private double price;
        private String flag;
        private String goodsDesc;
        private String images;
        private long goodsStock;
        private String goodsType;
    }
               

3.3 商品微服務

商品微服務是服務提供者,頁面靜态化微服務是服務的消費者

建立商品微服務hyq-service-product,繼承hyq-parent

  1. 在商品微服務的pom檔案中,引入公共元件坐标
    <dependency>
        <groupId>com.hyq</groupId>
        <artifactId>hyq-service-common</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
               
  2. 在yml檔案中配置端口、應用名、資料庫連接配接等資訊
    server:
      port: 9000 # 後期該微服務多執行個體,9000(10個以内)
    
    spring:
      application:
        name: hyq-service-product
      datasource:
        url: jdbc:mysql://localhost:3306/hyq?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&useSSL=false
        driver-class-name: com.mysql.jdbc.Driver
        username: root
        password: root
               
  3. Mapper接口開發
    package com.hyq.product.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.hyq.common.pojo.Products;
    
    /**
     * 現在使用的Mybatis-plus元件,該元件是Mybatis的加強版
     * 能夠與SpringBoot進行非常友好的整合,對比Mybatis架構隻有使用便捷的改變
     * 沒有具體功能的改變
     * 具體使用:讓具體的Mapper接口繼承BaseMapper即可
     */
    public interface ProductMapper extends BaseMapper<Products> {
    }
    
               
  4. serive層開發

    ProductService 接口

    package com.hyq.product.service;
    
    import com.hyq.common.pojo.Products;
    
    public interface ProductService {
    
        public Products findById(Integer productId);
    }
    
               
    ProductServiceImpl 實作類
    package com.hyq.product.service.impl;
    
    import com.hyq.common.pojo.Products;
    import com.hyq.product.mapper.ProductMapper;
    import com.hyq.product.service.ProductService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    
    @Service
    public class ProductServiceImpl implements ProductService {
    
        @Autowired
        private ProductMapper productMapper;
    
        /**
         * 根據商品ID查詢商品對象
         * @param productId
         * @return
         */
        @Override
        public Products findById(Integer productId) {
            return productMapper.selectById(productId);
        }
    }
    
               
  5. controller層開發
    package com.hyq.product.controller;
    
    import com.hyq.common.pojo.Products;
    import com.hyq.product.service.ProductService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("/product")
    public class ProductController {
    
        @Autowired
        private ProductService productService;
    
        @RequestMapping("/query/{id}")
        public Products query(@PathVariable Integer id){
            return productService.findById(id);
        }
    }
    
               
  6. 啟動類
    package com.hyq.product;
    
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    @MapperScan("com.hyq.product.mapper")
    public class ProductApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(ProductApplication.class);
        }
    }
    
               

3.4 頁面靜态化微服務

  1. 在pom檔案中,引入公共元件依賴
    <?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>hyq-parent</artifactId>
            <groupId>com.hyq</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>hyq-service-page</artifactId>
    
        <properties>
            <maven.compiler.source>11</maven.compiler.source>
            <maven.compiler.target>11</maven.compiler.target>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>com.hyq</groupId>
                <artifactId>hyq-service-common</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
        </dependencies>
    </project>
               
  2. 在yml檔案中配置端口、應用名、資料庫連接配接等資訊
    server:
      port: 9100 # 後期該微服務多執行個體,端口從9100遞增(10個以内)
    
    spring:
      application:
        name: hyq-service-page
      datasource:
        url: jdbc:mysql://localhost:3306/hyq?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&useSSL=false
        driver-class-name: com.mysql.jdbc.Driver
        username: root
        password: root
               
  3. 編寫PageController,在PageController中調用商品微服務對應的URL
    package com.hyq.page.controller;
    
    import com.hyq.common.pojo.Products;
    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;
    import org.springframework.web.client.RestTemplate;
    
    @RestController
    @RequestMapping("/page")
    public class PageController {
        @Autowired
        private RestTemplate restTemplate;
        
        @GetMapping("/getData/{id}")
        public Products findDataById(@PathVariable Integer id){
            Products products =  restTemplate.getForObject("http://localhost:9000/product/query/"+id, Products.class);
            System.out.println("從hyq-service-product獲得product對象:"+products);
            return products;
        }
    }
    
               
  4. 編寫啟動類,注入RestTemplate
    package com.hyq.page;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.client.RestTemplate;
    
    @SpringBootApplication
    public class PageApplication {
        public static void main(String[] args) {
            SpringApplication.run(PageApplication.class);
        }
    
        @Bean
        public RestTemplate restTemplate(){
            return new RestTemplate();
        }
    }
    
               

第 4 節 案例代碼問題分析

我們在頁面靜态化微服務中使用RestTemplate調用商品微服務的商品狀态接口時(Restful API 接口)。在微服務分布式叢集環境下會存在什麼問題呢?怎麼解決?

存在的問題:

  1. 在服務消費者中,我們把url位址寫死到代碼中,不友善後期維護。
  2. 服務提供者隻有一個服務,即便服務提供者形成叢集,服務消費者還需要自己實作負載均衡。
  3. 在服務消費者中,不清楚服務提供者的狀态。
  4. 服務消費者調用服務提供者時候,如果出現故障能否及時發現不向使用者抛出異常頁面?
  5. RestTemplate這種請求調用方式是否還有優化空間?能不能類似于Dubbo那樣玩?
  6. 這麼多的微服務統一認證如何實作?
  7. 配置檔案每次都修改好多個很麻煩!?

上述分析出的問題,其實就是微服務架構中必然面臨的一些問題:

  1. 服務管理:自動注冊與發現、狀态監管
  2. 服務負載均衡
  3. 熔斷
  4. 遠端過程調用
  5. 網關攔截、路由轉發
  6. 統一認證
  7. 集中式配置管理,配置資訊實時自動更新

這些問題,Spring Cloud 體系都有解決方案,後續我們會逐個學習。

第四部分 第一代 Spring Cloud 核心元件

說明:上面提到網關元件Zuul性能一般,未來将退出Spring Cloud 生态圈,是以我們直接講解GateWay,在課程章節規劃時,我們就把GateWay劃分到第一代Spring Cloud 核心元件這一部分了。

各元件整體結構如下:

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

從形式上來說,Feign一個頂三,Feign = RestTemplate + Ribbon + Hystrix

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

第 1 節 Eureka服務注冊中心

常用的服務注冊中心:Eureka、Nacos、Zookeeper、Consul

1.1 關于服務注冊中心

注意:服務注冊中心本質上是為了解耦服務提供者和服務消費者。

服務消費者 ==> 服務提供者

服務消費者 ==> 服務注冊中心 ==> 服務提供者

對于任何一個微服務,原則上都應存在或者支援多個提供者(比如商品微服務部署多個執行個體),這是由微服務的分布式屬性決定的。

更進一步,為了支援彈性擴、縮容特性,一個微服務的提供者的數量和分布往往是動态變化的,也是無法預先确定的。是以,原本在單體應用階段常用的靜态LB機制就不再适用了,需要引入額外的元件來管理微服務提供者的注冊與發現,而這個元件就是服務注冊中心。

1.1.1注冊中心實作原理

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

分布式微服務架構中,服務注冊中心用于存儲服務提供者位址資訊、服務釋出相關的屬性資訊,消費者通過主動查詢和被動通知的方式擷取服務提供者的位址資訊,而不再需要通過寫死方式得到提供者的位址資訊。消費者隻需要知道目前系統釋出了那些服務,而不需要知道服務具體存在于什麼位置,這就是透明化路由。

  1. 服務提供者啟動
  2. 服務提供者将相關服務資訊主動注冊到注冊中心
  3. 服務消費者擷取服務注冊資訊:

    pull模式:服務消費者可以主動拉取可用的服務提供者清單

    push模式:服務消費者訂閱服務(當服務提供者有變化時,注冊中心也會主動推送更新後的服務清單給消費者

  4. 服務消費者直接調用服務提供者

    另外,注冊中心也需要完成服務提供者的健康監控,當發現服務提供者失效時需要及時剔除;

1.1.2主流服務中心對比

  • Zookeeper

    Dubbo + Zookeeper

    Zookeeper它是一個分布式服務架構,是Apache Hadoop 的一個子項目,它主要是用來解決分布式應用中經常遇到的一些資料管理問題,如:統一命名服務、狀态同步服務、叢集管理、分布式應用配置項的管理等。

    簡單來說zookeeper本質 = 存儲 + 監聽通知。

    Zookeeper 用來做服務注冊中心,主要是因為它具有節點變更通知功能,隻要用戶端監聽相關服務節點,服務節點的所有變更,都能及時的通知到監聽用戶端,這樣作為調用方隻要使用Zookeeper 的用戶端就能實作服務節點的訂閱和變更通知功能了,非常友善。另外,Zookeeper可用性也可以,因為隻要半數以上的選舉節點存活,整個叢集就是可用的,最少節點數為3。

  • Eureka

    由Netflix開源,并被Pivatal內建到SpringCloud體系中,它是基于 RestfulAPI 風格開發的服務注冊與發現元件。

  • Consul

    Consul是由HashiCorp基于Go語言開發的支援多資料中心分布式高可用的服務釋出和注冊服務軟體, 采用Raft算法保證服務的一緻性,且支援健康檢查。

  • Nacos

    Nacos是一個更易于建構雲原生應用的動态服務發現、配置管理和服務管理平台。簡單來說Nacos 就是 注冊中心 + 配置中心的組合,幫助我們解決微服務開發必會涉及到的服務注冊 與發現,服務配置,服務管理等問題。Nacos 是 Spring Cloud Alibaba 核心元件之一,負責服務注冊與發現,還有配置。

    Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

    CAP定理又稱CAP原則,指的是在一個分布式系統中,Consistency(一緻性)、 Availability(可用性)、Partition tolerance(分區容錯性),最多隻能同時三個特性中的兩個,三者不可兼得。

    P:分區容錯性:分布式系統在遇到某節點或網絡分區故障的時候,仍然能夠對外提供滿足一緻性或可用

    性的服務(一定的要滿足的)

    C:資料一緻性:all nodes see the same data at the same time

    A:高可用:Reads and writes always succeed

CAP不可能同時滿足三個,要麼是AP,要麼是CP

1.2 服務注冊中心元件 Eureka

服務注冊中心的一般原理、對比了主流的服務注冊中心方案,目光聚焦Eureka。

  • Eureka 基礎架構
    Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件
  • Eureka 互動流程及原理
    Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件
    Eureka 包含兩個元件:Eureka Server 和 Eureka Client,Eureka Client是一個Java用戶端,用于簡化與Eureka Server的互動;Eureka Server提供服務發現的能力,各個微服務啟動時,會通過EurekaClient向Eureka Server 進行注冊自己的資訊(例如網絡資訊),Eureka Server會存儲該服務的資訊;
    1. 圖中us-east-1c、us-east-1d,us-east-1e代表不同的區也就是不同的機房
    2. 圖中每一個Eureka Server都是一個叢集。
    3. 圖中Application Service作為服務提供者向Eureka Server中注冊服務,Eureka Server接受到注冊事件會在叢集和分區中進行資料同步,Application Client作為消費端(服務消費者)可以從Eureka Server中擷取到服務注冊資訊,進行服務調用。
    4. 微服務啟動後,會周期性地向Eureka Server發送心跳(預設周期為30秒,預設Eureka Server 90S會将還沒有續約的給剔除)以續約自己的資訊
    5. Eureka Server在一定時間内沒有接收到某個微服務節點的心跳,Eureka Server将會登出該微服務節點(預設90秒)
    6. 每個Eureka Server同時也是Eureka Client,多個Eureka Server之間通過複制的方式完成服務注冊清單的同步
    7. Eureka Client會緩存Eureka Server中的資訊。即使所有的Eureka Server節點都宕掉,服務消費者依然可以使用緩存中的資訊找到服務提供者
    Eureka通過心跳檢測、健康檢查和用戶端緩存等機制,提高系統的靈活性、可伸縮性和高可用性。

1.3 搭建單例Eureka Server服務注冊中心

實作過程:

  1. 單執行個體Eureka Server—>通路管理界面
  2. 服務提供者(商品微服務注冊到叢集)
  3. 服務消費者(頁面靜态化微服務注冊到Eureka/從Eureka Server擷取服務資訊)
  4. 完成調用

1.3.1、搭建Eureka Server服務 hyq-cloud-eureka

hyq-parent中引入Spring Cloud 依賴

Spring Cloud 是一個綜合的項目,下面有很多子項目,比如eureka子項目

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Greenwich.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
           

1.3.2、hyq-cloud-eureka工程pom.xml中引入依賴

<?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>hyq-parent</artifactId>
        <groupId>com.hyq</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>hyq-cloud-eureka</artifactId>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>
    
    <dependencies>
        <!--Eureka server依賴-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>

</project>
           

注意:在父工程的pom檔案中手動引入jaxb的jar,因為Jdk9之後預設沒有加載該子產品,Eureka

Server使用到,是以需要手動導入,否則EurekaServer服務無法啟動

<!--引入Jaxb,開始-->
<dependency>
	<groupId>com.sun.xml.bind</groupId>
	<artifactId>jaxb-core</artifactId>
	<version>2.2.11</version>
</dependency>
<dependency>
	<groupId>javax.xml.bind</groupId>
	<artifactId>jaxb-api</artifactId>
</dependency>
<dependency>
	<groupId>com.sun.xml.bind</groupId>
	<artifactId>jaxb-impl</artifactId>
	<version>2.2.11</version>
</dependency>
<dependency>
	<groupId>org.glassfish.jaxb</groupId>
	<artifactId>jaxb-runtime</artifactId>
	<version>2.2.10-b140310.1920</version>
</dependency>
<dependency>
	<groupId>javax.activation</groupId>
	<artifactId>activation</artifactId>
	<version>1.1.1</version>
</dependency>
<!--引入Jaxb,結束-->
           

1.3.3、在yml檔案中配置Eureka server服務端口,服務名等資訊

server:
  port: 9200 #Eureka server服務端口

spring:
  application:
    name: hyq-cloud-eureka-server # 應用名稱,會在Eureka中作為服務的id辨別(serviceId)

eureka:
  instance:
    hostname: localhost
  client:
    service-url: # 用戶端與EurekaServer互動的位址,如果是叢集,也需要寫其它Server的位址
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
    register-with-eureka: false # 自己就是服務不需要注冊自己
    fetch-registry: false #自己就是服務不需要從Eureka Server擷取服務資訊,預設為true,置為false
           

1.3.4、編寫啟動類,聲明目前服務為Eureka注冊中心

package com.hyq.eureka;

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

@SpringBootApplication
// 聲明本項目是一個Eureka服務
@EnableEurekaServer
public class EurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class);
    }
}

           

1.3.5、通路http://127.0.0.1:9200

如果看到如下頁面(Eureka注冊中心背景),則表明EurekaServer釋出成功

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

1.3.6、商品微服務和頁面靜态化微服務注冊到Eureka

pom檔案中添加Eureka Client依賴

<!--Eureka client-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
           

yml配置Eureka服務端資訊

eureka:
  client:
    service-url:
      defaultZone: http://localhost:9200/eureka
    fetch-registry: true
    register-with-eureka: true
  instance:
    #使用ip注冊,否則會使用主機名注冊了(此處考慮到對老版本的相容,新版本經過實驗都是ip)
    prefer-ip-address: true
    #自定義執行個體顯示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@[email protected]
           

修改啟動類

package com.hyq.product;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@MapperScan("com.hyq.product.mapper")
@EnableDiscoveryClient //等價于 @EnableEurekaClient
public class ProductApplication {

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

           

1.4 搭建Eureka Server 高可用叢集

在網際網路應用中,服務執行個體很少有單個的。

如果EurekaServer隻有一個執行個體,該執行個體挂掉,正好微服務消費者本地緩存清單中的服務執行個體也不可用,那麼這個時候整個系統都受影響。

在生産環境中,我們會配置Eureka Server叢集實作高可用。Eureka Server叢集之中的節點通過點對點(P2P)通信的方式共享服務系統資料庫。我們開啟兩台 Eureka Server 以搭建叢集。

由于是在個人計算機中進行測試很難模拟多主機的情況,Eureka配置server叢集時需要執行host位址。 是以需要修改個人電腦中host位址:

windows 作業系統下:C:\Windows\System32\drivers\etc\host

127.0.0.1 HyqCloudEurekaServerA
127.0.0.1 HyqCloudEurekaServerB
           

将hyq-cloud-eureka複制一份為hyq-cloud-eureka9201

  • 修改 hyq-cloud-eureka-server 工程中的yml配置檔案

    9200

    server:
      port: 9200 #Eureka server服務端口
    
    spring:
      application:
        name: hyq-cloud-eureka-server # 應用名稱,會在Eureka中作為服務的id辨別(serviceId)
    
    eureka:
      instance:
        hostname: HyqCloudEurekaServerA
      client:
        service-url: # 用戶端與EurekaServer互動的位址,如果是叢集,也需要寫其它Server的位址
          defaultZone: http://HyqCloudEurekaServerB:9201/eureka
        register-with-eureka: true
        fetch-registry: true
               
    9201
    server:
    port: 9201 #Eureka server服務端口
    
    spring:
      application:
        name: hyq-cloud-eureka-server # 應用名稱,會在Eureka中作為服務的id辨別(serviceId)
    
    eureka:
      instance:
        hostname: HyqCloudEurekaServerB
      client:
        service-url: # 用戶端與EurekaServer互動的位址,如果是叢集,也需要寫其它Server的位址
          defaultZone: http://HyqCloudEurekaServerB:9200/eureka
        register-with-eureka: true
        fetch-registry: true
               
    商品微服務:
    server:
      port: 9000 # 後期該微服務多執行個體,9000(10個以内)
    spring:
      application:
        name: hyq-service-product
      datasource:
        url: jdbc:mysql://localhost:3306/hyq?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&useSSL=false
        driver-class-name: com.mysql.jdbc.Driver
        username: root
        password: root
    eureka:
      client:
        service-url:
          #把 eureka 叢集中的所有 url 都填寫了進來,也可以隻寫一台,因為各個 eureka server 可以同步系統資料庫
          defaultZone: http://hyqcloudeurekaservera:9200/eureka/,http://hyqcloudeurekaserverb:9201/eureka/
        fetch-registry: true
        register-with-eureka: true
      instance:
        #使用ip注冊,否則會使用主機名注冊了(此處考慮到對老版本的相容,新版本經過實驗都是ip)
        prefer-ip-address: true
        #自定義執行個體顯示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress
        instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@[email protected]
               
    頁面靜态化微服務:
    server:
      port: 9100 # 後期該微服務多執行個體,端口從9100遞增(10個以内)
    
    spring:
      application:
        name: hyq-service-page
      datasource:
        url: jdbc:mysql://localhost:3306/hyq?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC&useSSL=false
        driver-class-name: com.mysql.jdbc.Driver
        username: root
        password: root
    
    eureka:
      client:
        service-url:
          #把 eureka 叢集中的所有 url 都填寫了進來,也可以隻寫一台,因為各個 eureka server 可以同步系統資料庫
          defaultZone: http://hyqcloudeurekaservera:9200/eureka/,http://hyqcloudeurekaserverb:9201/eureka/
        register-with-eureka: true
        fetch-registry: true
      instance:
        #使用ip注冊,否則會使用主機名注冊了(此處考慮到對老版本的相容,新版本經過實驗都是ip)
        prefer-ip-address: true
        #自定義執行個體顯示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress
        instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@[email protected]
               
  • 服務消費者調用服務提供者

    改造頁面靜态化微服務:之前是直接通過RestTemplate寫死URL進行調用,現在通過Eureka方式進行調用。

    package com.hyq.page.controller;
    
    import com.hyq.common.pojo.Products;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.client.ServiceInstance;
    import org.springframework.cloud.client.discovery.DiscoveryClient;
    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;
    import org.springframework.web.client.RestTemplate;
    
    import java.util.List;
    
    @RestController
    @RequestMapping("/page")
    public class PageController {
        @Autowired
        private RestTemplate restTemplate;
    
        @Autowired
        private DiscoveryClient discoveryClient;
    
        @GetMapping("/getData/{id}")
        public Products findDataById(@PathVariable Integer id) {
            //1.獲得Eureka中注冊的hyq-service-product執行個體集合
            List<ServiceInstance> instances = discoveryClient.getInstances("hyq-service-product");
            //2.獲得執行個體集合中的第一個
            ServiceInstance instance = instances.get(0);
            //3.根據執行個體資訊拼接IP位址
            String host = instance.getHost();
            int port = instance.getPort();
            String url = "http://" + host + ":" + port + "/product/query/" + id;
            //4.調用
            Products products = restTemplate.getForObject(url, Products.class);
            System.out.println("從hyq-service-product獲得product對象:" + products);
            return products;
        }
    }
    
               

1.5 Eureka細節詳解

1.5.1 Eureka中繼資料詳解

Eureka的中繼資料有兩種:标準中繼資料和自定義中繼資料。
标準中繼資料:主機名、IP位址、端口号等資訊,這些資訊都會被釋出在服務系統資料庫中,用于服務之間的調用。
自定義中繼資料:可以使用eureka.instance.metadata-map配置,符合KEY/VALUE的存儲格式。這些中繼資料可以在遠端用戶端中通路。
           

類似于

eureka:
  # 标準中繼資料
  client:
    service-url:
      defaultZone: http://hyqcloudeurekaservera:9200/eureka/,http://hyqcloudeurekaserverb:9201/eureka/
    register-with-eureka: true
    fetch-registry: true
  instance:
    prefer-ip-address: true
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@[email protected]
    # 自定義中繼資料
    metadata-map:
      ip: 192.168.10.123
      port: 1010
      user: admin
      password: 123456

           

我們可以在程式中可以使用DiscoveryClient 擷取指定微服務的所有中繼資料資訊

package com.hyq.page.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Map;
import java.util.Set;

@RestController
@RequestMapping("/metadata")
public class MetadataController {

    @Autowired
    private DiscoveryClient discoveryClient;

    @RequestMapping("/show")
    public String showMetaData(){
        String result = "";
        List<ServiceInstance> instances = discoveryClient.getInstances("hyq-service-page");
        for (ServiceInstance instance:instances) {
            //擷取服務中繼資料
            Map<String, String> metadata = instance.getMetadata();
            Set<Map.Entry<String, String>> entries = metadata.entrySet();
            for (Map.Entry<String,String> entry : entries){
                String key = entry.getKey();
                String value = entry.getValue();
                result+="key:"+key+",value:"+value;
            }
        }
        return result;
    }

}

           

标準中繼資料

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

自定義中繼資料

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

1.5.2 Eureka用戶端詳解

服務提供者(也是Eureka用戶端)要向EurekaServer注冊服務,并完成服務續約等工作

  • 服務注冊詳解(服務提供者)
    1. 當我們導入了eureka-client依賴坐标,配置Eureka服務注冊中心位址
    2. 服務在啟動時會向注冊中心發起注冊請求,攜帶服務中繼資料資訊
    3. Eureka注冊中心會把服務的資訊儲存在Map中。
  • 服務續約詳解(服務提供者)

    服務每隔30秒會向注冊中心續約(心跳)一次(也稱為報活),如果沒有續約,租約在90秒後到期,然後服務會被失效。每隔30秒的續約操作我們稱之為心跳檢測

    1. Eureka Client :30S續約一次,在Eureka Server更新自己的狀态 (Client端進行配置)
    2. Eureka Server:90S還沒有進行續約,将該微服務執行個體從服務系統資料庫(Map)剔除 (Client端進行配置)
    3. Eureka Client: 30S拉取服務最新的系統資料庫并緩存到本地 (Client端進行配置)
    往往不需要我們調整這兩個配置
    eureka:
      instance:
        # 租約到期,服務時效時間,預設值90秒,服務超過90秒沒有發生心跳,EurekaServer會将服務從清單移除
        lease-expiration-duration-in-seconds: 30
        # 租約續約間隔時間,預設30秒
        lease-renewal-interval-in-seconds: 30
               
  • 擷取服務清單(服務系統資料庫)詳解(服務消費者)

    每隔30秒服務會從注冊中心中拉取一份服務清單,這個時間可以通過配置修改。往往不需要我們調整

    eureka:
      client:
        # 每隔多久拉取一次服務清單
        registry-fetch-interval-seconds: 30
               
    1. 服務消費者啟動時,從 EurekaServer服務清單擷取隻讀備份,緩存到本地
    2. 每隔30秒,會重新擷取并更新資料
    3. 每隔30秒的時間可以通過配置eureka.client.registry-fetch-interval-seconds修改

1.5.3 Eureka服務端詳解

  • 服務下線:
    1. 當服務正常關閉操作時,會發送服務下線的REST請求給EurekaServer。
    2. 服務中心接受到請求後,将該服務置為下線狀态
  • 失效剔除:

    Eureka Server會定時(間隔值是eureka.server.eviction-interval-timer-in-ms,預設60s)進行檢查,如果發現執行個體在在一定時間(此值由用戶端設定的eureka.instance.lease-expiration-duration-in-seconds定義,預設值為90s)内沒有收到心跳,則會登出此執行個體。

  • 自我保護機制:

    自我保護模式正是一種針對網絡異常波動的安全保護措施,使用自我保護模式能使Eureka叢集更加的健壯、穩定的運作。

    自我保護機制的工作機制是:如果在15分鐘内超過85%的用戶端節點都沒有正常的心跳,那麼Eureka就認為用戶端與注冊中心出現了網絡故障,Eureka Server自動進入自我保護機制,此時會出現以下幾種情況:

    1. Eureka Server不再從注冊清單中移除因為長時間沒收到心跳而應該過期的服務。
    2. Eureka Server仍然能夠接受新服務的注冊和查詢請求,但是不會被同步到其它節點上,保證目前節點依然可用。
    3. 當網絡穩定時,目前Eureka Server新的注冊資訊會被同步到其它節點中。
    是以Eureka Server可以很好的應對因網絡故障導緻部分節點失聯的情況,而不會像ZK那樣如果有一半不可用的情況會導緻整個叢集不可用而變成癱瘓。
  • 為什麼會有自我保護機制?

    預設情況下,如果Eureka Server在一定時間内(預設90秒)沒有接收到某個微服務執行個體的心跳,Eureka Server将會移除該執行個體。但是當網絡分區故障發生時,微服務與Eureka Server之間無法正常通信,而微服務本身是正常運作的,此時不應該移除這個微服務,是以引入了自我保護機制。

    服務中心頁面會顯示如下提示資訊

    Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件
    我們在單機測試的時候很容易滿足心跳失敗比例在 15 分鐘之内低于 85%,這個時候就會觸發 Eureka的保護機制,一旦開啟了保護機制(預設開啟),則服務注冊中心維護的服務執行個體就不是那麼準确了,此時我們通過修改Eureka Server的配置檔案來關閉保護機制,這樣可以確定注冊中心中不可用的執行個體被及時的剔除(不推薦)。
    eureka:
      server:
        enable-self-preservation: false # 關閉自我保護模式(預設為打開)
               
    經驗:建議生産環境打開自我保護機制

第 2 節 Ribbon負載均衡

2.1 關于負載均衡

負載均衡一般分為伺服器端負載均衡和用戶端負載均衡

所謂伺服器端負載均衡,比如Nginx、F5這些,請求到達伺服器之後由這些負載均衡器根據一定的算法将請求路由到目标伺服器處理。

所謂用戶端負載均衡,比如我們要說的Ribbon,服務消費者用戶端會有一個伺服器位址清單,調用方在請求前通過一定的負載均衡算法選擇一個伺服器進行通路,負載均衡算法的執行是在請求用戶端進行。

Ribbon是Netflix釋出的負載均衡器。Eureka一般配合Ribbon進行使用,Ribbon利用從Eureka中讀取到服務資訊,在調用服務提供者提供的服務時,會根據一定的算法進行負載。

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

2.2 Ribbon進階應用

需求:

複制商品微服務9001,在9000和9001編寫Controller,傳回服務執行個體端口。

Page微服務中通過負載均衡政策調用hyq-service-product的controller

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

在微服務中使用Ribbon不需要額外導入依賴坐标,微服務中引入過eureka-client相關依賴,會自動引入Ribbon相關依賴坐标。

代碼中使用如下,在RestTemplate上添加對應注解即可

package com.hyq.page;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

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

    @Bean
    @LoadBalanced  //Ribbon負載均衡
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

           

建立hyq-serivce-product-9001微服務,建立ServerConfigController,定義方法傳回目前微服務所使用的容器端口号

修改服務提供者api傳回值,傳回目前執行個體的端口号,便于觀察負載情況

package com.hyq.product.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/server")
public class ServerConfigController {

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

    @RequestMapping("/query")
    public String findServerPort(){
        return serverPort ;
    }
}
           

在頁面靜态化微服務中調用hyq-service-product下的資源路徑:http://hyq-server-product/ser

ver/query

@RequestMapping("/getPort")
public String getProductServerPort(){
    String url ="http://hyq-service-product/server/query";
    return restTemplate.getForObject(url , String.class);
}
           

第一次通路:

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

第二次通路:

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

2.3 Ribbon負載均衡政策

Ribbon内置了多種負載均衡政策,内部負責複雜均衡的頂級接口為:com.netflix.loadbalancer.IRule

接口代碼:

package com.netflix.loadbalancer;

public interface IRule {
    Server choose(Object var1);

    void setLoadBalancer(ILoadBalancer var1);

    ILoadBalancer getLoadBalancer();
}

           
負載均衡政策 描述
RoundRobinRule:輪詢政策 預設超過10次擷取到的server都不可用,會傳回一個空的server
RandomRule:随機政策 如果随機到的server為null或者不可用的話,會while不停的循環選取
RetryRule:重試政策 一定時限内循環重試。預設繼承RoundRobinRule,也支援自定義注入,RetryRule會在每次選取之後,對選舉的server進行判斷,是否為null,是否alive,并且在500ms内會不停的選取判斷。而RoundRobinRule失效的政策是超過10次,RandomRule是沒有失效時間的概念,隻要serverList沒都挂。
BestAvailableRule:最小連接配接數政策 周遊serverList,選取出可用的且連接配接數最小的一個server。該算法裡面有一個LoadBalancerStats的成員變量,會存儲所有server的運作狀況和連接配接數。如果選取到的server為null,那麼會調用RoundRobinRule重新選取。
AvailabilityFilteringRule:可用過濾政策 擴充了輪詢政策,會先通過預設的輪詢選取一個server,再去判斷該server是否逾時可用,目前連接配接數是否超限,都成功再傳回。
ZoneAvoidanceRule:區域權衡政策(預設政策) 擴充了輪詢政策,繼承了2個過濾器:ZoneAvoidancePredicate和AvailabilityPredicate,除了過濾逾時和連結數過多的server,還會過濾掉不符合要求的zone區域裡面的所有節點, 在一個區域/機房内的服務執行個體中輪詢。先過濾再輪詢

修改負載均衡政策:

  • 配置檔案方式
    # 給某個微服務配置負載均衡規則
    hyq-service-product:
      ribbon:
        NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #随機政策
               
  • 代碼方式
    @Bean
    public IRule randomRule(){
        return new RandomRule();  
    }
               
    注意,一般用預設的負載均衡規則,不做修改

2.4 Ribbon核心源碼剖析

Ribbon工作原理:

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件
  • 老規矩:SpringCloud充分利用了SpringBoot的自動裝配特點,找spring.factories配置檔案
    Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件
    ctrl + 單擊 進入RibbonAutoConfiguration類的源碼:
    Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件
  • LoadBalancerAutoConfiguration 類中配置

    1、裝配驗證:

    Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件
    2、自動注入:
    Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

    3、注入restTemplate定制器:

    為restTemplate對象設定loadBalancerInterceptor

    Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

到這裡,我們明白,添加了注解的RestTemplate對象會被添加一個攔截器LoadBalancerInterceptor,該攔截器就是後續攔截請求進行負載處理的。

第 3 節Hystrix熔斷器

屬于一種容錯機制

3.1 微服務中的雪崩效應

當山坡積雪内部的内聚力抗拒不了它所受到的重力拉引時,便向下滑動,引起大量雪體崩塌,人們把這種自然現象稱作雪崩。

微服務中,一個請求可能需要多個微服務接口才能實作,會形成複雜的調用鍊路。

服務雪崩效應: 是一種因“服務提供者的不可用”(原因)導緻“服務調用者不可用”(結果),并将不可用逐漸放大的現象。

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件
Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件
Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件
Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件
扇入:代表着該微服務被調用的次數,扇入大,說明該子產品複用性好
扇出:該微服務調用其他微服務的個數,扇出大,說明業務邏輯複雜
扇入大是一個好事,扇出大不一定是好事
           

在微服務架構中,一個應用可能會有多個微服務組成,微服務之間的資料互動通過遠端過程調用完成。這就帶來一個問題,假設微服務A調用微服務B和微服務C,微服務B和微服務C又調用其它的微服務,這就是所謂的“扇出”。如果扇出的鍊路上某個微服務的調用響應時間過長或者不可用,對微服務A的調用就會占用越來越多的系統資源,進而引起系統崩潰,所謂的“雪崩效應”。

如圖中所示,最下遊商品微服務響應時間過長,大量請求阻塞,大量線程不會釋放,會導緻伺服器資源耗盡,最終導緻上遊服務甚至整個系統癱瘓。

形成原因:

服務雪崩的過程可以分為三個階段:

  1. 服務提供者不可用
  2. 重試加大請求流量
  3. 服務調用者不可用

服務雪崩的每個階段都可能由不同的原因造成:

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

3.2 雪崩效應解決方案

從可用性可靠性着想,為防止系統的整體緩慢甚至崩潰,采用的技術手段;

下面,我們介紹三種技術手段應對微服務中的雪崩效應,這三種手段都是從系統可用性、可靠性角度出發,盡量防止系統整體緩慢甚至癱瘓。

  • 服務熔斷

    熔斷機制是應對雪崩效應的一種微服務鍊路保護機制。我們在各種場景下都會接觸到熔斷這兩個字。高壓電路中,如果某個地方的電壓過高,熔斷器就會熔斷,對電路進行保護。股票交易中,如果股票指數過高,也會采用熔斷機制,暫停股票的交易。同樣,在微服務架構中,熔斷機制也是起着類似的作用。當扇對外連結路的某個微服務不可用或者響應時間太長時,熔斷該節點微服務的調用,進行服務的降級,快速傳回錯誤的響應資訊。當檢測到該節點微服務調用響應正常後,恢複調用鍊路。

    注意:

    1. 服務熔斷重點在“斷”,切斷對下遊服務的調用
    2. 服務熔斷和服務降級往往是一起使用的,Hystrix就是這樣。
  • 服務降級

    通俗講就是整體資源不夠用了,先将一些不關緊的服務停掉(調用我的時候,給你傳回一個預留的值,也叫做兜底資料),待渡過難關高峰過去,再把那些服務打開。

    服務降級一般是從整體考慮,就是當某個服務熔斷之後,伺服器将不再被調用,此刻用戶端可以自己準備一個本地的fallback回調,傳回一個預設值,這樣做,雖然服務水準下降,但好歹可用,比直接挂掉要強。

  • 服務限流

    服務降級是當服務出問題或者影響到核心流程的性能時,暫時将服務屏蔽掉,待高峰或者問題解決後再打開;但是有些場景并不能用服務降級來解決,比如秒殺業務這樣的核心功能,這個時候可以結合服務限流來限制這些場景的并發/請求量

    限流措施也很多,比如

    • 限制總并發數(比如資料庫連接配接池、線程池)
    • 限制瞬時并發數(如nginx限制瞬時并發連接配接數)
    • 限制時間視窗内的平均速率(如Guava的RateLimiter、nginx的limit_req子產品,限制每秒的平均速率)
    • 限制遠端接口調用速率、限制MQ的消費速率等

3.3 Hystrix簡介

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

[來自官網] Hystrix(豪豬),宣言“defend your application”是由Netflix開源的一個延遲和容錯庫,用于隔離通路遠端系統、服務或者第三方庫,防止級聯失敗,進而提升系統的可用性與容錯性。Hystrix主要通過以下幾點實作延遲和容錯。

  • 包裹請求:使用HystrixCommand包裹對依賴的調用邏輯。 頁面靜态化微服務方法(@HystrixCommand 添加Hystrix控制)
  • 跳閘機制:當某服務的錯誤率超過一定的門檻值時,Hystrix可以跳閘,停止請求該服務一段時間。
  • 資源隔離:Hystrix為每個依賴都維護了一個小型的線程池(艙壁模式)。如果該線程池已滿, 發往該依賴的請求就被立即拒絕,而不是排隊等待,進而加速失敗判定。
  • 監控:Hystrix可以近乎實時地監控運作名額和配置的變化,例如成功、失敗、逾時、以及被拒絕的請求等。
  • 回退機制:當請求失敗、逾時、被拒絕,或當斷路器打開時,執行回退邏輯。回退邏輯由開發人員自行提供,例如傳回一個預設值。
  • 自我修複:斷路器打開一段時間後,會自動進入“半開”狀态(探測服務是否可用,如還是不可用,再次退回打開狀态)。

3.4 Hystrix應用

3.4.1.熔斷處理

目的: 商品微服務長時間沒有響應,服務消費者—>頁面靜态化微服務快速失敗給使用者提示

  • 引入依賴: 服務消費者工程(靜态化微服務)中引入Hystrix依賴坐标(也可以添加在父工程中)
    <!--熔斷器Hystrix-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    </dependency>
               
  • 開啟熔斷: 服務消費者工程(靜态化微服務)的啟動類中添加熔斷器開啟注解

    @EnableCircuitBreaker

    package com.hyq.page;
    
    import com.netflix.loadbalancer.IRule;
    import com.netflix.loadbalancer.RandomRule;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
    import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
    import org.springframework.cloud.client.loadbalancer.LoadBalanced;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.client.RestTemplate;
    
    /**
     * 注解簡化寫法
     * @SpringCloudApplication = @[email protected][email protected]
     */
    @SpringBootApplication
    @EnableEurekaClient //@EnableDiscoveryClient
    @EnableCircuitBreaker // 開啟熔斷
    public class PageApplication {
        public static void main(String[] args) {
            SpringApplication.run(PageApplication.class);
        }
    
        @Bean
        @LoadBalanced  //Ribbon負載均衡
        public RestTemplate restTemplate(){
            return new RestTemplate();
        }
    
    //    @Bean
    //    public IRule randomRule(){
    //        return new RandomRule();
    //    }
    
    }
    
               
  • 定義服務降級處理方法: 業務方法上使用@HystrixCommand的fallbackMethod屬性關聯到服務降級處理方法
    /**
     * 提供者模拟處理逾時,調用方法添加Hystrix控制
     */
    // 使用@HystrixCommand注解進行熔斷控制
    @HystrixCommand(
            // 線程池辨別,要保持唯一,不唯一的話就共用了
            threadPoolKey = "getProductServerPort2",
            // 線程池細節屬性配置
            threadPoolProperties = {
                    @HystrixProperty(name="coreSize",value = "1"), // 線程數
                    @HystrixProperty(name="maxQueueSize",value="20") // 等待隊列長度
            },
            // commandProperties熔斷的一些細節屬性配置
            commandProperties = {
                    // 每一個屬性都是一個HystrixProperty
                    @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds" , value = "2000")
            }
    )
    @RequestMapping("/getPort2")
    public String getProductServerPort2(){
        String url ="http://hyq-service-product/server/query";
        return restTemplate.getForObject(url , String.class);
    }
               
  • 商品微服務模拟逾時操作
    package com.hyq.product.controller;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("/server")
    public class ServerConfigController {
    
        @Value("${server.port}")
        private String serverPort;
    
        @RequestMapping("/query")
        public String findServerPort(){
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return serverPort ;
        }
    
    }
    
               

3.4.2降級處理

配置@HystrixCommand注解,定義降級處理方法

/**
 * 提供者模拟處理逾時,調用方法添加Hystrix控制
 */
// 使用@HystrixCommand注解進行熔斷控制
@HystrixCommand(
        // 線程池辨別,要保持唯一,不唯一的話就共用了
        threadPoolKey = "getProductServerPort2",
        // 線程池細節屬性配置
        threadPoolProperties = {
                @HystrixProperty(name="coreSize",value = "1"), // 線程數
                @HystrixProperty(name="maxQueueSize",value="20") // 等待隊列長度
        },
        // commandProperties熔斷的一些細節屬性配置
        commandProperties = {
                // 每一個屬性都是一個HystrixProperty
                @HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds" , value = "2000"),
                // hystrix進階配置,定制工作過程細節
                // 統計時間視窗定義
                @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "8000"),
                // 統計時間視窗内的最小請求數
                @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "2"),
                // 統計時間視窗内的錯誤數量百分比門檻值
                @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
                // 自我修複時的活動視窗長度
                @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "3000")
        },
        fallbackMethod = "myFallBack" // 回退方法
)
@RequestMapping("/getPort2")
public String getProductServerPort2(){
    String url ="http://hyq-service-product/server/query";
    return restTemplate.getForObject(url , String.class);
}
/** 
 * 定義回退方法,傳回預設預設值
 * 注意:該方法形參和傳回值與原始方法保持一緻
 */
public String myFallBack() {
    return "-1"; // 兜底資料
}
           

3.5 Hystrix艙壁模式

即:線程池隔離政策

如果不進行任何設定,所有熔斷方法使用一個Hystrix線程池(10個線程),那麼這樣的話會導緻問題,這個問題并不是扇對外連結路微服務不可用導緻的,而是我們的線程機制導緻的,如果方法A的請求把10個線程都用了,方法2請求處理的時候壓根都沒法去通路B,因為沒有線程可用,并不是B服務不可用。

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

為了避免問題服務請求過多導緻正常服務無法通路,Hystrix 不是采用增加線程數,而是單獨的為每一個控制方法建立一個線程池的方式,這種模式叫做“艙壁模式",也是線程隔離的手段。

3.6 Hystrix工作流程與進階應用

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件
1、當調用出現問題時,開啟一個時間窗(10s)
2、在這個時間窗内,統計調用次數是否達到最小請求數?
	 如果沒有達到,則重置統計資訊,回到第1步
 	 如果達到了,則統計失敗的請求數占所有請求數的百分比,是否達到門檻值?
		如果達到,則跳閘(不再請求對應服務)
		如果沒有達到,則重置統計資訊,回到第1步
3、如果跳閘,則會開啟一個活動視窗(預設5s),每隔5s,Hystrix會讓一個請求通過,到達那個問題服務,
   看是否調用成功,如果成功,重置斷路器回到第1步,如果失敗,回到第3步
           
/**
 * 8秒鐘内,請求次數達到2個,并且失敗率在50%以上,就跳閘
 * 跳閘後活動視窗設定為3s
 */
@HystrixCommand(
        commandProperties = {
                //統計視窗時間的設定
                @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "8000"),
                //統計視窗内的最小請求數
                @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "2"),
                //統計視窗内錯誤請求門檻值的設定 50%
                @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
                //自我修複的活動視窗時間
                @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "3000")
        }
)
           

我們上述通過注解進行的配置也可以配置在配置檔案中:

# 配置熔斷政策:
hystrix:
  command:
    default:
      circuitBreaker:
        # 強制打開熔斷器,如果該屬性設定為true,強制斷路器進入打開狀态,将會拒絕所有的請求。預設false關閉的
        forceOpen: false
        # 觸發熔斷錯誤比例門檻值,預設值50%
        errorThresholdPercentage: 50
        # 熔斷後休眠時長,預設值5秒
        sleepWindowInMilliseconds: 3000
        # 熔斷觸發最小請求次數,預設值是20
        requestVolumeThreshold: 2
      execution:
        isolation:
          thread:
            # 熔斷逾時設定,預設為1秒
            timeoutInMilliseconds: 2000
           

基于springboot的健康檢查觀察跳閘狀态(自動投遞微服務暴露健康檢查細節)

#springboot中暴露健康檢查等斷點接口
management:
  endpoints:
    web:
      exposure:
        exclude: *
  #暴露健康接口的細節
  endpoint:
    health:
      show-details: always
           

通路健康檢查接口:http://localhost:9100/actuator/health

Hystrix 線程池隊列配置案例:

有一次在生産環境,突然出現了很多筆還款單被挂起,後來排查原因,發現是内部系統調用時出現了Hystrix調用異常。在開發過程中,因為核心線程數設定的比較大,沒有出現這種異常。放到了測試環境,偶爾有出現這種情況。

後來調整maxQueueSize屬性,确實有所改善。可沒想到在生産環境跑了一段時間後卻又出現這種了情況,此時我第一想法就是去檢視maxQueueSize屬性,可是maxQueueSize屬性是設定值了。

當時就比較納悶了,為什麼maxQueueSize屬性不起作用,後來通過檢視官方文檔發現Hystrix還有一個queueSizeRejectionThreshold屬性,這個屬性是控制隊列最大門檻值的,而Hystrix預設隻配置了5個,是以就算我們把maxQueueSize的值設定再大,也是不起作用的。兩個屬性必須同時配置

hystrix:
  threadpool:
    default:
      coreSize: 10 #并發執行的最大線程數,預設10
      maxQueueSize: 1000 #BlockingQueue的最大隊列數,預設值-1
      queueSizeRejectionThreshold: 800 #即使maxQueueSize沒有達到,達到queueSizeRejectionThreshold該值後,請求也會被拒絕,預設值5
           

正确的配置案例:

  • 将核心線程數調低,最大隊列數和隊列拒絕門檻值的值都設定大一點:
hystrix:
  threadpool:
    default:
      coreSize: 10 
      maxQueueSize: 1500
      queueSizeRejectionThreshold: 1000 
           

第 4 節 Feign遠端調用元件

在之前的案例中,服務消費者調用服務提供者的時候使用RestTemplate技術。

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

4.1 Feign簡介

Feign是Netflix開發的一個輕量級RESTful的HTTP服務用戶端(用它來發起請求,遠端調用的),是以Java接口注解的方式調用Http請求,而不用像Java中通過封裝HTTP請求封包的方式直接調用,Feign被廣泛應用在Spring Cloud 的解決方案中。

類似于Dubbo,服務消費者拿到服務提供者的接口,然後像調用本地接口方法一樣去調用,實際發出的是遠端的請求。

Feign可幫助我們更加便捷,優雅的調用HTTP API:不需要我們去拼接url然後呢調用restTemplate的api,在SpringCloud中,使用Feign非常簡單,建立一個接口(在消費者–服務調用方這一端),并在接口上添加一些注解,代碼就完成了

SpringCloud對Feign進行了增強,使Feign支援了SpringMVC注解(OpenFeign)

本質:封裝了Http調用流程,更符合面向接口化的程式設計習慣,類似于Dubbo的服務調用

4.2 Feign配置應用

在服務調用者工程(消費)建立接口(添加注解)

(效果)Feign = RestTemplate+Ribbon+Hystrix

  • 服務消費者工程(頁面靜态化微服務)中引入Feign依賴(或者父類工程)
    <!-- Feign依賴       -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
               
  • 服務消費者工程(靜态化微服務)啟動類使用注解@EnableFeignClients添加Feign支援
    @SpringBootApplication
    @EnableEurekaClient //@EnableDiscoveryClient
    @EnableFeignClients // 開啟Feign 
    public class PageApplication {
        public static void main(String[] args) {
            SpringApplication.run(PageApplication.class);
        }
    }
               
    注意:此時去掉Hystrix熔斷的支援注解@EnableCircuitBreaker即可包括引入的依賴,因為Feign會自動引入
  • 在消費者微服務中建立Feign接口
    package com.hyq.page.feign;
    
    import com.hyq.common.pojo.Products;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @FeignClient("hyq-service-product")
    public interface ProductFeign {
        /**
         * 通過id擷取商品資訊
         * @param id
         * @return
         */
        @RequestMapping("/product/query/{id}")
        public Products query(@PathVariable Integer id);
        /**
         * 獲得端口号
         * @return
         */
        @RequestMapping("/server/query")
        public String findServerPort();
    }
               
    注意:
    1. @FeignClient注解的name屬性用于指定要調用的服務提供者名稱,和服務提供者yml檔案中spring.application.name保持一緻
    2. 接口中的接口方法,就好比是遠端服務提供者Controller中的Hander方法(隻不過如同本地調用了),那麼在進行參數綁定的時,可以使用@PathVariable、@RequestParam、@RequestHeader等,這也是OpenFeign對SpringMVC注解的支援,但是需要注意value必須設定,否則會抛出異常
    3. @FeignClient(name = “hyq-service-product”),name在消費者微服務中隻能出現一次。(更新Spring Boot 2.1.0 Spring Cloud Greenwich.M1 版本後,在2個Feign接口類内定義相同的名字, @FeignClient(name = 相同的名字 就會出現報錯,在之前的版本不會提示報錯),是以最好将調用一個微服務的資訊都定義在一個Feign接口中。
  • 改造PageController中原有的調用方式
    package com.hyq.page.controller;
    
    import com.hyq.common.pojo.Products;
    import com.hyq.page.feign.ProductFeign;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.client.ServiceInstance;
    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;
    
    import java.util.List;
    
    @RestController
    @RequestMapping("/page")
    public class PageController {
    
        @Autowired
        private ProductFeign productFeign ;
    
        @GetMapping("/getData/{id}")
        public Products findDataById(@PathVariable Integer id) {
            return productFeign.query(id);
        }
    
        @RequestMapping("/getPort")
        public String getProductServerPort() {
            return productFeign.findServerPort();
        }
    }
               

4.3 Feign對負載均衡的支援

Feign 本身已經內建了Ribbon依賴和自動配置,是以我們不需要額外引入依賴,可以通過

ribbon.xx 來進 行全局配置,也可以通過服務名.ribbon.xx 來對指定服務進行細節配置配置(參考之前,此處略)

Feign預設的請求處理逾時時長1s,有時候我們的業務确實執行的需要一定時間,那麼這個時候,我們就需要調整請求處理逾時時長,Feign自己有逾時設定,如果配置Ribbon的逾時,則會以Ribbon的為準

hyq-service-product:
  ribbon:
    ConnectTimeout: 2000 #請求連接配接逾時時間
    ReadTimeout: 5000 #請求處理逾時時間
    OkToRetryOnAllOperations: true #對所有操作都進行重試
    ####根據如上配置,當通路到故障請求的時候,它會再嘗試通路一次目前執行個體(次數由MaxAutoRetries配置),
    ####如果不行,就換一個執行個體進行通路,如果還不行,再換一次執行個體通路(更換次數由MaxAutoRetriesNextServer配置),
    ####如果依然不行,傳回失敗資訊。
    MaxAutoRetries: 0 #對目前選中執行個體重試次數,不包括第一次調用
    MaxAutoRetriesNextServer: 0 #切換執行個體的重試次數
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #随機政策
           

4.4 Feign對熔斷器的支援

  1. 在Feign用戶端工程配置檔案(application.yml)中開啟Feign對熔斷器的支援
    # 開啟Feign的熔斷功能
    feign:
      hystrix:
        enabled: true
               

    Feign的逾時時長設定那其實就上面Ribbon的逾時時長設定

    Hystrix逾時設定(就按照之前Hystrix設定的方式就OK了)

    注意:

    1. 開啟Hystrix之後,Feign中的方法都會被進行一個管理了,一旦出現問題就進入對應的回退邏輯處理
    2. 針對逾時這一點,目前有兩個逾時時間設定(Feign/hystrix),熔斷的時候是根據這兩個時間的最小值來進行的,即處理時長超過最短的那個逾時時間了就熔斷進入回退降級邏輯
    # 配置熔斷政策:
    hystrix:
      command:
        default:
          circuitBreaker:
            # 強制打開熔斷器,如果該屬性設定為true,強制斷路器進入打開狀态,将會拒絕所有的請求。預設false關閉的
            forceOpen: false
            # 觸發熔斷錯誤比例門檻值,預設值50%
            errorThresholdPercentage: 50
            # 熔斷後休眠時長,預設值5秒
            sleepWindowInMilliseconds: 3000
            # 熔斷觸發最小請求次數,預設值是20
            requestVolumeThreshold: 2
          execution:
            isolation:
              thread:
                # 熔斷逾時設定,預設為1秒
                timeoutInMilliseconds: 2000
               
  2. 自定義FallBack處理類(需要實作FeignClient接口)
    package com.hyq.page.feign.fallback;
    
    import com.hyq.common.pojo.Products;
    import com.hyq.page.feign.ProductFeign;
    import org.springframework.stereotype.Component;
    
    @Component
    public class ProductFeignFallBack implements ProductFeign {
        @Override
        public Products query(Integer id) {
            return null;
        }
    
        @Override
        public String findServerPort() {
            return "-1";
        }
    }
               
    @FeignClient(name = "hyq-service-product" , fallback = ProductFeignFallBack.class)
    public interface ProductFeign {
     //其餘代碼無變動  省略
    }
               

4.5 Feign對請求壓縮和響應壓縮的支援

Feign 支援對請求和響應進行GZIP壓縮,以減少通信過程中的性能損耗。通過下面的參數 即可開啟請求與響應的壓縮功能:

feign:
  hystrix:
    enabled: true
  #開啟請求和響應壓縮
  compression:
    request:
      mime-types: text/html,application/xml,application/json # 設定壓縮的資料類型,此處也是預設值
      enabled: true # 預設不開啟
      min-request-size: 2048 # 設定觸發壓縮的大小下限,此處也是預設值
    response:
      enabled: true # 預設不開啟
           

第 5 節 GateWay網關元件

網關:微服務架構中的重要組成部分

區域網路中就有網關這個概念,區域網路接收或者發送資料出去通過這個網關,比如用Vmware虛拟機

軟體搭建虛拟機叢集的時候,往往我們需要選擇IP段中的一個IP作為網關位址。

我們學習的GateWay–>Spring Cloud GateWay(它隻是衆多網關解決方案中的一種)

5.1 GateWay簡介

Spring Cloud GateWay是Spring Cloud的一個全新項目,目标是取代Netflix Zuul,它基于

Spring5.0+SpringBoot2.0+WebFlux(基于高性能的Reactor模式響應式通信架構Netty,異步非阻塞模型)等技術開發,性能高于Zuul,官方測試,GateWay是Zuul的1.6倍,旨在為微服務架構提供一種簡單有效的統一的API路由管理方式。

Spring Cloud GateWay不僅提供統一的路由方式(反向代理)并且基于 Filter(定義過濾器對請求過濾,完成一些功能) 鍊的方式提供了網關基本的功能,例如:鑒權、流量控制、熔斷、路徑重寫、日志監控等。

網關在架構中的位置

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

5.2 GateWay核心概念

Spring Cloud GateWay天生就是異步非阻塞的,基于Reactor模型(同步非阻塞的I/O多路複用機制)

一個請求—>網關根據一定的條件比對—比對成功之後可以将請求轉發到指定的服務位址;而在這個過程中,我們可以進行一些比較具體的控制(限流、日志、黑白名單)

  • 路由(route): 網關最基礎的部分,也是網關比較基礎的工作單元。路由由一個ID、一個目标URL(最終路由到的位址)、一系列的斷言(比對條件判斷)和Filter過濾器(精細化控制)組成。如果斷言為true,則比對該路由。
  • 斷言(predicates):參考了Java8中的斷言java.util.function.Predicate,開發人員可以比對Http請求中的所有内容(包括請求頭、請求參數等)(類似于nginx中的location比對一樣),如果斷言與請求相比對則路由。
  • 過濾器(filter):一個标準的Spring webFilter,使用過濾器,可以在請求之前或者之後執行業務邏輯。

5.3 GateWay如何工作

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

Spring 官方介紹:

用戶端向Spring Cloud GateWay送出請求,然後在GateWay Handler Mapping中找到與請求相比對的路由,将其發送到GateWay Web Handler;Handler再通過指定的過濾器鍊來将請求發送到我們實際的服務執行業務邏輯,然後傳回。過濾器之間用虛線分開是因為過濾器可能會在發送代理請求之前(pre)或者之後(post)執行業務邏輯。

Filter在“pre”類型過濾器中可以做參數校驗、權限校驗、流量監控、日志輸出、協定轉換等,在“post”類型的過濾器中可以做響應内容、響應頭的修改、日志的輸出、流量監控等。

5.4 GateWay應用

使用網關對靜态化微服務進行代理(添加在它的上遊,相當于隐藏了具體微服務的資訊,對外暴露的是網關)

  • 建立工程hyq-cloud-gateway-server導入依賴

    GateWay不需要使用web子產品,它引入的是WebFlux(類似于SpringMVC)

    <?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">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.hyq</groupId>
        <artifactId>hyq-cloud-gateway-server</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <!--spring boot 父啟動器依賴-->
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.1.6.RELEASE</version>
        </parent>
    
        <properties>
            <maven.compiler.source>11</maven.compiler.source>
            <maven.compiler.target>11</maven.compiler.target>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-commons</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
            <!--GateWay 網關-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-gateway</artifactId>
            </dependency>
            <!--引入webflux-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-webflux</artifactId>
            </dependency>
            <!--日志依賴-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-logging</artifactId>
            </dependency>
            <!--測試依賴-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <!--lombok工具-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.4</version>
                <scope>provided</scope>
            </dependency>
            <!--引入Jaxb,開始-->
            <dependency>
                <groupId>com.sun.xml.bind</groupId>
                <artifactId>jaxb-core</artifactId>
                <version>2.2.11</version>
            </dependency>
            <dependency>
                <groupId>javax.xml.bind</groupId>
                <artifactId>jaxb-api</artifactId>
            </dependency>
            <dependency>
                <groupId>com.sun.xml.bind</groupId>
                <artifactId>jaxb-impl</artifactId>
                <version>2.2.11</version>
            </dependency>
            <dependency>
                <groupId>org.glassfish.jaxb</groupId>
                <artifactId>jaxb-runtime</artifactId>
                <version>2.2.10-b140310.1920</version>
            </dependency>
            <dependency>
                <groupId>javax.activation</groupId>
                <artifactId>activation</artifactId>
                <version>1.1.1</version>
            </dependency>
            <!--引入Jaxb,結束-->
            <!-- Actuator可以幫助你監控和管理Spring Boot應用-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-actuator</artifactId>
            </dependency>
            <!--熱部署-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <optional>true</optional>
            </dependency>
            <!--鍊路追蹤-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-sleuth</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-zipkin</artifactId>
            </dependency>
        </dependencies>
    
        <dependencyManagement>
            <!--spring cloud依賴版本管理-->
            <dependencies>
                <dependency>
                    <groupId>org.springframework.cloud</groupId>
                    <artifactId>spring-cloud-dependencies</artifactId>
                    <version>Greenwich.RELEASE</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
        <build>
            <plugins>
                <!--編譯插件-->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>11</source>
                        <target>11</target>
                        <encoding>utf-8</encoding>
                    </configuration>
                </plugin>
                <!--打包插件-->
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    </project>
               
    注意:不要引入starter-web子產品,需要引入web-flux
  • application.yml 配置檔案内容
    server:
      port: 9300
    
    eureka:
      client:
        service-url:
          #把 eureka 叢集中的所有 url 都填寫了進來,也可以隻寫一台,因為各個 eureka server 可以同步系統資料庫
          defaultZone: http://hyqcloudeurekaservera:9200/eureka/,http://hyqcloudeurekaserverb:9201/eureka/
        register-with-eureka: true
        fetch-registry: true
      instance:
        #使用ip注冊,否則會使用主機名注冊了(此處考慮到對老版本的相容,新版本經過實驗都是ip)
        prefer-ip-address: true
        #自定義執行個體顯示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress
        instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@[email protected]
    
    spring:
      application:
        name: hyq-cloud-gateway
      # 網關的配置
      cloud:
        gateway:
          routes: #配置路由
            - id: service-page-router
              uri: http://127.0.0.1:9100
              predicates: #當斷言成功後,交給某一個微服務處理時使用的是轉發
                - Path=/page/**
            - id: service-product-router
              uri: http://127.0.0.1:9000
              predicates:
                - Path=/product/**
              filters:
                # http://127.0.0.1:9300/product/service/port --> /service/port --> 商品微服務
                - StripPrefix=1 #去掉uri中的第一部分,是以就要求我們通過網關通路的時候,把uri的第一部分設定為product,從uri的第二部分開始才是真正的uri
               
    Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件
    Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件
    Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件
  • 啟動類
    package com.hyq.gateway;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
    
    @SpringBootApplication
    @EnableEurekaClient
    public class GatewayApplication {
        public static void main(String[] args) {
            SpringApplication.run(GatewayApplication.class);
        }
    }
               
  • 測試

    http://localhost:9300/product/server/query

    Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件
    http://localhost:9300/page/getPort
    Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

5.5 GateWay路由規則詳解

Spring Cloud GateWay 幫我們内置了很多 Predicates功能,實作了各種路由比對規則(通過

Header、請求參數等作為條件)比對到對應的路由。

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件
  • 時間點後比對
    spring:
      cloud:
        gateway:
          routes: 
            - id: after_route  
              uri: https://example.org
              predicates:
                - After=2017-01-20T17:42:47.789-07:00[America/Denver]
               
  • 時間點前比對
    spring:
      cloud:
        gateway:
          routes: 
            - id: before_route  
              uri: https://example.org
              predicates:
                - Before=2017-01-20T17:42:47.789-07:00[America/Denver]
               
  • 時間區間比對
    spring:
      cloud:
        gateway:
          routes: 
            - id: between_route
              uri: https://example.org
              predicates:
                - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
               
  • 指定Cookie正則比對指定值
    spring:
      cloud:
        gateway:
          routes: 
            - id: cookie_route
              uri: https://example.org
              predicates:
                - Cookie=chocolate, ch.p
               
  • 指定Header正則比對指定值
    spring:
      cloud:
        gateway:
          routes: 
            - id: header_route
              uri: https://example.org
              predicates:
                - Header=X-Request-Id, \d+
               
  • 請求Host比對指定值
    spring:
      cloud:
        gateway:
          routes: 
            - id: host_route
              uri: https://example.org
              predicates:
                - Host=**.somehost.org,**.anotherhost.org
               
  • 請求Method比對指定請求方式
    spring:
      cloud:
        gateway:
          routes: 
            - id: method_route
              uri: https://example.org
              predicates:
                - Method=GET,POST
               
  • 請求路徑正則比對
    spring:
      cloud:
        gateway:
          routes: 
            - id: path_route
              uri: https://example.org
              predicates:
                - Path=/red/{segment},/blue/{segment}
               
  • 請求包含某參數
    spring:
      cloud:
        gateway:
          routes: 
            - id: query_route
              uri: https://example.org
              predicates:
                - Query=green
               
  • 請求包含某參數并且參數值比對正規表達式
    spring:
      cloud:
        gateway:
          routes: 
            - id: query_route
              uri: https://example.org
              predicates:
                - Query=red, gree.
               
  • 遠端位址比對
    spring:
      cloud:
        gateway:
          routes: 
            - id: remoteaddr_route
              uri: https://example.org
              predicates:
                - RemoteAddr=192.168.1.1/24
               

5.6 GateWay動态路由詳解

GateWay支援自動從注冊中心中擷取服務清單并通路,即所謂的動态路由

實作步驟如下

  1. pom.xml中添加注冊中心用戶端依賴(因為要擷取注冊中心服務清單,eureka用戶端已經引入)
  2. 動态路由配置
    spring:
      application:
        name: hyq-cloud-gateway
      # 網關的配置
      cloud:
        gateway:
          routes: #配置路由
            - id: service-page-router
              uri: lb://hyq-service-page
              predicates: 
                - Path=/page/**
            - id: service-product-router
              uri: lb://hyq-service-product
              predicates:
                - Path=/product/**
              filters:
                - StripPrefix=1 
               
    注意:動态路由設定時,uri以 lb: //開頭(lb代表從注冊中心擷取服務),後面是需要轉發到的服務名稱

5.7 GateWay過濾器

5.7.1 GateWay過濾器簡介

從過濾器生命周期(影響時機點)的角度來說,主要有兩個pre和post:

生命周期時機點 作用
pre 這種過濾器在請求被路由之前調用。我們可利用這種過濾器實作身份驗證、在叢集中選擇 請求的微服務、記錄調試資訊等。
post 這種過濾器在路由到微服務以後執行。這種過濾器可用來為響應添加标準的 HTTPHeader、收集統計資訊和名額、将響應從微服務發送給用戶端等。

從過濾器類型的角度,Spring Cloud GateWay的過濾器分為GateWayFilter和GlobalFilter兩種

過濾器類型 影響範圍
GateWayFilter 應用到單個路由路由上
GlobalFilter 應用到所有的路由上

如Gateway Filter可以去掉url中的占位後轉發路由,比如

predicates:
  - Path=/product/**
filters:
  - StripPrefix=1
           

注意:GlobalFilter全局過濾器是程式員使用比較多的過濾器,我們主要講解這種類型

5.7.2 自定義全局過濾器實作IP通路限制(黑白名單)

請求過來時,判斷發送請求的用戶端的ip,如果在黑名單中,拒絕通路

自定義GateWay全局過濾器時,我們實作Global Filter接口即可,通過全局過濾器可以實作黑白名單、限流等功能。

package com.hyq.gateway.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;

@Slf4j
@Component
public class BlackListFilter implements GlobalFilter, Ordered {
    // 模拟黑名單(實際可以去資料庫或者redis中查詢)
    private static List<String> blackList = new ArrayList<>();

    static {
        blackList.add("0:0:0:0:0:0:0:1"); // 模拟本機位址
        blackList.add("127.0.0.1");
    }

    /**
     * 過濾器核心方法
     *
     * @param exchange 封裝了request和response對象的上下文
     * @param chain    網關過濾器鍊(包含全局過濾器和單路由過濾器)
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("....BlackListFilter....");
        // 思路:擷取用戶端ip,判斷是否在黑名單中,在的話就拒絕通路,不在的話就放行
        // 從上下文中取出request和response對象
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        // 從request對象中擷取用戶端ip
        String clientIp = request.getRemoteAddress().getHostString();
        // 拿着clientIp去黑名單中查詢,存在的話就決絕通路
        if (blackList.contains(clientIp)) {
            // 決絕通路,傳回
            response.setStatusCode(HttpStatus.UNAUTHORIZED); // 狀态碼
            log.info("=====>IP:" + clientIp + " 在黑名單中,将被拒絕通路!");
            String data = "Request be denied!";
            DataBuffer wrap = response.bufferFactory().wrap(data.getBytes());
            return response.writeWith(Mono.just(wrap));
        }
        // 合法請求,放行,執行後續的過濾器
        return chain.filter(exchange);
    }

    /**
     * 傳回值表示目前過濾器的順序(優先級),數值越小,優先級越高
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

           

5.8 GateWay高可用

網關作為非常核心的一個部件,如果挂掉,那麼所有請求都可能無法路由處理,是以我們需要做GateWay的高可用。

GateWay的高可用很簡單: 可以啟動多個GateWay執行個體來實作高可用,在GateWay的上遊使用Nginx等負載均衡裝置進行負載轉發以達到高可用的目的。

啟動多個GateWay執行個體(假如說兩個,一個端口9002,一個端口9003),剩下的就是使用Nginx等完成負載代理即可。示例如下:

#配置多個GateWay執行個體
upstream gateway {
	server 127.0.0.1:9002;
	server 127.0.0.1:9003;
}
location / {
	proxy_pass http://gateway;
}
           

第 6 節 Spring Cloud Config 分布式配置中心

6.1 分布式配置中心應用場景

往往,我們使用配置檔案管理一些配置資訊,比如application.yml

單體應用架構,配置資訊的管理、維護并不會顯得特别麻煩,手動操作就可以,因為就一個工程;

微服務架構,因為我們的分布式叢集環境中可能有很多個微服務,我們不可能一個一個去修改配置然後重新開機生效,在一定場景下我們還需要在運作期間動态調整配置資訊,比如:根據各個微服務的負載情況,動态調整資料源連接配接池大小,我 們希望配置内容發生變化的時候,微服務可以自動更新。

場景總結如下:

  1. 集中配置管理,一個微服務架構中可能有成百上千個微服務,是以集中配置管理是很重要的(一次修改、到處生效)
  2. 不同環境不同配置,比如資料源配置在不同環境(開發dev,測試test,生産prod)中是不同的
  3. 運作期間可動态調整。例如,可根據各個微服務的負載情況,動态調整資料源連接配接池大小等配置修改後可自動更新
  4. 如配置内容發生變化,微服務可以自動更新配置

那麼,我們就需要對配置檔案進行集中式管理,這也是分布式配置中心的作用。

6.2 Spring Cloud Config

6.2.1 Config簡介

Spring Cloud Config是一個分布式配置管理方案,包含了 Server端和 Client端兩個部分。

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件
  • Server 端:提供配置檔案的存儲、以接口的形式将配置檔案的内容提供出去,通過使用@EnableConfigServer注解在 Spring boot 應用中非常簡單的嵌入
  • Client 端:通過接口擷取配置資料并初始化自己的應用

6.2.2 Config分布式配置應用

說明:Config Server是集中式的配置服務,用于集中管理應用程式各個環境下的配置。 預設使用Git存儲配置檔案内容,也可以SVN。

比如,我們要對“靜态化微服務或者商品微服務”的application.yml進行管理(區分開發環境

(dev)、測試環境(test)、生産環境(prod))

  1. 登入gitee,建立項目hyq-config
  2. 上傳yml配置檔案,命名規則如下:

    {application}-{profile}.yml 或者 {application}-{profile}.properties

    其中,application為應用名稱,profile指的是環境(用于區分開發環境,測試環境、生産環境等)

    示例:hyq-service-page-dev.yml、hyq-service-page-test.yml、hyq-service-page-prod.yml

    Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件
  3. 建構Config Server統一配置中心
    • 建立SpringBoot工程,引入依賴坐标(需要注冊自己到Eureka)
      <?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>hyq-parent</artifactId>
              <groupId>com.hyq</groupId>
              <version>1.0-SNAPSHOT</version>
          </parent>
          <modelVersion>4.0.0</modelVersion>
      
          <artifactId>hyq-cloud-config</artifactId>
      
          <properties>
              <maven.compiler.source>11</maven.compiler.source>
              <maven.compiler.target>11</maven.compiler.target>
          </properties>
      
          <dependencies>
              <!--eureka client 用戶端依賴引入-->
              <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
              </dependency>
              <!--config配置中心服務端-->
              <dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-config-server</artifactId>
              </dependency>
          </dependencies>
      
      </project>
                 
    • 配置啟動類,使用注解@EnableConfigServer開啟配置中心伺服器功能
      package com.hyq.config;
      
      import org.springframework.boot.SpringApplication;
      import org.springframework.boot.autoconfigure.SpringBootApplication;
      import org.springframework.cloud.config.server.EnableConfigServer;
      import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
      
      @SpringBootApplication
      @EnableEurekaClient
      @EnableConfigServer
      public class ConfigApplication {
          public static void main(String[] args) {
              SpringApplication.run(ConfigApplication.class);
          }
      }
      
                 
    • application.yml配置
      server:
        port: 9400
      
      eureka:
        client:
          service-url:
            #把 eureka 叢集中的所有 url 都填寫了進來,也可以隻寫一台,因為各個 eureka server 可以同步系統資料庫
            defaultZone: http://hyqcloudeurekaservera:9200/eureka/,http://hyqcloudeurekaserverb:9201/eureka/
          register-with-eureka: true
          fetch-registry: true
        instance:
          #使用ip注冊,否則會使用主機名注冊了(此處考慮到對老版本的相容,新版本經過實驗都是ip)
          prefer-ip-address: true
          #自定義執行個體顯示格式,加上版本号,便于多版本管理,注意是ip-address,早期版本是ipAddress
          instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@[email protected]
      
      spring:
        application:
          name: hyq-service-config
        cloud:
          config:
            server:
              git:
                uri: https://****.git #配置git服務位址
                username: # 使用者名
                password: # 密碼
                search-paths:
                  - hyq-config
            # 讀取分支
            label: master
                 
    • 測試

      http://localhost:9400/master/application-dev.yml

      master:分支名稱

      application-dev.yml:檔案名稱

  4. 建構Client用戶端(在已有頁面靜态化微服務基礎上)

    案例實作:在hyq-service-page微服務中動态擷取config server的配置資訊

    • 已有工程中添加依賴坐标
      <dependency>
      	<groupId>org.springframework.cloud</groupId>
      	<artifactId>spring-cloud-config-client</artifactId>
      </dependency>
                 
    • application.yml修改為bootstrap.yml配置檔案

      bootstrap.yml是系統級别的,優先級比application.yml高,應用啟動時會檢查這個配置檔案,在這個配置檔案中指定配置中心的服務位址,會自動拉取所有應用配置并且啟用。

      (主要是把與統一配置中心連接配接的配置資訊放到bootstrap.yml)

      注意:需要統一讀取的配置資訊,從配置中心擷取

      spring:
        cloud:
          config:
            #config用戶端配置,和ConfigServer通信,并告知ConfigServer希望擷取的配置資訊在哪個檔案中
            name: hyq-service-page # 檔案名
            profile: dev #字尾名稱
            label: master #分支名稱
            uri: http://localhost:9400 #ConfigServer配置中心位址
                 
    • ConfigClientController
      package com.hyq.page.controller;
      
      import org.springframework.beans.factory.annotation.Value;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.RestController;
      
      @RestController
      @RequestMapping("/config")
      public class ConfigClientController {
          
          @Value("${mysql.user}")
          private String mysqlUser;
          
          @Value("${person.name}")
          private String personName;
      
          @RequestMapping("/remote")
          public String findRemoteConfig() {
              return mysqlUser + " " + personName;
          }
      }
                 
      Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件
    • 測試

      http://localhost:9100/config/remote

      Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

6.3 Config配置手動重新整理

不用重新開機微服務,隻需要手動的做一些其他的操作(通路一個位址/refresh)重新整理,之後再通路即可

此時,用戶端取到了配置中心的值,但當我們修改gitee上面的值時,服務端(Config Server)能實時擷取最新的值,但用戶端(Config Client)讀的是緩存,無法實時擷取最新值。Spring Cloud已經為我們解決了這個問題,那就是用戶端使用post去觸發refresh,擷取最新資料。

  1. Client用戶端添加依賴springboot-starter-actuator
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
               
  2. Client用戶端bootstrap.yml中添加配置(暴露通信端點)
    management:
      endpoints:
        web:
          exposure:
            include: refresh
    ############################
    #也可以暴露所有的端口
    management:
      endpoints:
        web:
          exposure:
            include: "*"
               
  3. Client用戶端使用到配置資訊的類上添加@RefreshScope
    package com.hyq.page.controller;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.cloud.context.config.annotation.RefreshScope;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    @RequestMapping("/config")
    @RefreshScope //手動重新整理
    public class ConfigClientController {
    
        @Value("${mysql.user}")
        private String mysqlUser;
    
        @Value("${person.name}")
        private String personName;
    
        @RequestMapping("/remote")
        public String findRemoteConfig() {
            return mysqlUser + " " + personName;
        }
    }
               
  4. 手動向Client用戶端發起POST請求,http://localhost:9100/actuator/refresh,重新整理配置資訊
    Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

    注意:手動重新整理方式避免了服務重新開機

    思考:可否使用廣播機制,一次通知,處處生效,友善大範圍配置自動重新整理?

6.4 Config配置自動更新

實作一次通知,處處生效

在微服務架構中,我們可以結合消息總線(Bus)實作分布式配置的自動更新(Spring Cloud

Config + Spring Cloud Bus)

6.4.1 消息總線Bus

所謂消息總線Bus,即我們經常會使用MQ消息代理建構一個共用的Topic,通過這個Topic連接配接各個微服務執行個體,MQ廣播的消息會被所有在注冊中心的微服務執行個體監聽和消費。換言之就是通過一個主題連接配接各個微服務,打通脈絡。

Spring Cloud Bus(基于MQ的,支援RabbitMq/Kafka) 是Spring Cloud中的消息總線方案,

Spring Cloud Config + Spring Cloud Bus 結合可以實作配置資訊的自動更新。

Spring Cloud 微服務講義第一部分 微服務架構第二部分 Spring Cloud 綜述第三部分 案例準備第四部分 第一代 Spring Cloud 核心元件

6.4.2 Spring Cloud Config + Spring Cloud Bus 實作自動重新整理

MQ消息代理,我們還選擇使用RabbitMQ,ConfigServer和ConfigClient都添加都消息總線的支援以及與RabbitMq的連接配接資訊

  1. Config Server服務端和用戶端添加消息總線支援
    <dependency>
    	<groupId>org.springframework.cloud</groupId>
    	<artifactId>spring-cloud-starter-bus-amqp</artifactId>
    </dependency>
               
  2. Config Server和用戶端添加配置
    spring:
      rabbitmq:
        host: # ip位址
        port: 5672 #端口号
        username: guest #使用者名
        password: guest # 密碼
               
  3. Config Server微服務暴露端口
    management:
      endpoints:
        web:
          exposure:
            include: bus-refresh
    #建議暴露所有的端口
    management:
      endpoints:
        web:
          exposure:
            include: "*"
               
  4. 重新開機各個服務,更改配置之後,向配置中心服務端發送post請求,各個用戶端配置即可自動重新整理

    http://localhost:9100/actuator/bus-refresh

  5. Config Client測試

    http://localhost:9100/config/remote

    在廣播模式下實作了一次請求,處處更新,如果我隻想定向更新呢?

    在發起重新整理請求的時候http://localhost:9006/actuator/bus-refresh/hyq-service-page:9100

    即為最後面跟上要定向重新整理的執行個體的 服務名:端口号即可