終於有人把 Spring Cloud-Nginx 架構的主要組件給講明白了

◆Spring Cloud+Nginx 架構的主要組件

==============================

以 crazy-springcloud 開發腳手架爲例,一個 Spring Cloud+Nginx 應用的架構如圖 1-1 所示。

圖 1-1 基於 Spring Cloud+Nginx 的應用架構

Nginx 作爲反向代理服務器,代理內部 Zuul 網關服務,通過 Nginx 自帶的負載均衡算法實現客戶端請求的代理轉發、負載均衡等功能。

Zuul 網關主要實現了微服務集羣內部的請求路由、負載均衡、統一校驗等功能。雖然在路由服務和負載均衡方面,Zuul 和 Nginx 的功能比較類似,但是 Zuul 是自身註冊到 Eureka/Nacos,通過微服務的 serviceID 實現微服務提供者之間的路由和轉發。

Eureka、Nacos 都是 Spring Cloud 技術體系中提供服務註冊與發現的中間件。Eureka 是 Netflix 開源的一款產品,提供了完整的服務註冊和發現,是 Spring Cloud“全家桶” 中的核心組件之一。

Nacos 是阿里巴巴推出來的一個開源項目,也是一個服務註冊與發現中間件,它用於完成服務的動態註冊、動態發現、服務管理,還兼具了配置管理的功能。Nacos 提供了一組簡單易用的特性集,用於實現動態服務發現、服務配置、服務元數據及流量管理。

由於新版本的 Eureka 已經閉源,而阿里巴巴的 Nacos 除了具備 Eureka 註冊中心功能外,還具備 Spring Cloud Config 配置中心的功能,因此大大地降低了使用和維護的成本。另外,Nacos 還具有分組隔離功能,一套 Nacos 集羣可以支撐多項目、多環境。綜合上述多個原因,在實際的開發場景中,推薦大家使用 Nacos。但是,本文出於學習目的,註冊中心和配置中心的內容還是介紹 Eureka+Config 組合,其實在原理上,Nacos 和 Eureka+Config 組合是差不多的。

除了一系列基礎設施中間件技術組件之外,微服務架構中大部分獨立業務模型都是以服務提供者的角色出現的。一般來說,系統可以按照各類業務模塊進行細粒度的微服務拆分,例如秒殺系統中的用戶、商品等,每個業務模塊拆分成一個微服務提供者 Provider 組件,作爲獨立應用程序進行啓動和執行。

在 Spring Cloud 生態中,微服務提供者 Provider 之間的遠程調用是通過 Feign+Ribbon+Hystrix 組合來完成的:Feign 用於完成 RPC 遠程調用的代理封裝;Ribbon 用於在客戶端完成各遠程目標服務實例之間的負載均衡;Hystrix 用於完成自動熔斷降級等多個維度的 RPC 保護。在 Nginx+Spring Cloud 架構中還存在一系列輔助中間件,包括日誌記錄、鏈路跟蹤、應用監控、JVM 性能指標、物理資源監控等等。本文並沒有對上述輔助中間件做專門的介紹。

◆Spring Cloud 和 Spring Boot 的版本選擇

Spring Cloud 是基於 Spring Boot 構建的,它們之間的版本有配套的對應關係。在構建項目時,要注意版本之間的這種對應關係,版本若對應不上則會出現問題。

Spring Cloud 和 Spring Boot 的版本配套關係如表 1-1 所示。

表 1-1 Spring Cloud 與 Spring Boot 的版本配套關係

表 1-1 Spring Cloud 與 Spring Boot 的版本配套關係

Spring Cloud 包含一系列子組件,如 Spring Cloud Config、Spring Cloud Netflix、Spring Cloud Openfeign 等,爲了防止與這些子組件的版本號混淆,Spring Cloud 的版本號全部使用英文單詞形式命名。具體來說,Spring Cloud 的版本號使用了英國倫敦地鐵站的名稱來命名,並按字母 A~Z 的次序發佈版本,它的第一個版本叫作 Angel,第二個版本叫作 Brixton,以此類推。另外,每個大版本在解決了一個嚴重的 Bug 後,Spring Cloud 會發佈一個 Service Release 版本(小版本),簡稱 SRX 版本,其中 X 是順序的編號,比如 Finchley.SR4 是 Finchley 大版本的第 4 個小版本。

大家做技術選型時非常喜歡用最高版本,但是對於 Spring 全家桶的選擇來說,高版本不一定是最佳選擇。比如,目前最高的 Spring CloudHoxton 版本是基於 Spring Boot 2.2 構建的,Spring Boot 2.2 又是基於 Spring Framework 5.2 構建的,也就是說,這是一次整體的、全方位的大版本升級。大家在項目上會用到非常多的第三方組件,總會有一些組件沒有來得及進行配套升級而不能兼容 Spring Boot 2.2 或 SpringFramework 5.2,如果貿然地進行基礎框架的整體升級,就會給項目開發帶來各種各樣的疑難雜症,甚至帶來潛在的線上 Bug。

除此之外,Spring Cloud 高版本推薦了不少自家的新組件,但是這些新組件沒有經過大規模實踐應用的考驗,其功能尚待豐富和完善。以負載均衡組件爲例,Spring Cloud Hoxton 推薦的自家組件 springcloud-loadbalancer 在功能上與 Ribbon 的負載均衡功能相比就弱很多。

Spring Cloud Finchley 到 Greenwich 版本的升級其實很小,可以說微乎其微,主要是提升了對 Java 11 的兼容性。然而,在當前的生產場景中 Java 8 纔是各大項目的主流選擇,另外 Java 11(2019 年 4 月之後的升級補丁)已經不完全免費了。當然,和 Java 11 一樣,Java 8 在 2019 年 4 月之後的補丁版本也面臨收費的問題。使用 Java 8 的理由是,自 2014 年 3 月 18 日發佈起至目前,Java 8 被廣泛使用,且被維護了這麼多年,已經非常成熟和穩定了。

綜上所述,本文選用了 Spring Cloud Finchley 作爲學習、研究和使用的版本,推薦使用的子版本爲 Finchley.SR4。具體的 Maven 依賴座標如下:

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

◆Spring Cloud 微服務開發所涉及的中間件

在基於 crazy-springcloud 腳手架(其他的腳手架類似)的微服務開發和自驗證過程中,所涉及的基礎中間件大致如下:

1.ZooKeeper

ZooKeeper 是一個開放源碼的分佈式協調應用程序,是大數據框架 Hadoop 和 HBase 的重要組件。在分佈式應用中,它能夠高可用地提供保

障數據一致性的很多基礎功能:分佈式鎖、選主、分佈式命名服務等。

在 crazy-springcloud 腳手架中,高性能分佈式 ID 生成器用到了 ZooKeeper。

2.Redis

Redis 是一個高性能的緩存數據庫。在高併發的場景下,Redis 可以對關係數據庫起到很好的緩衝作用;在提高系統的併發能力和響應速度方面,Redis 至關重要。crazy-springcloud 腳手架的分佈式 Session 用到了 Redis。

3.Eureka

Eureka 是 Netflix 開發的服務註冊和發現框架,它本身是一個 REST 服務提供者,主要用於定位運行在 AWS(Amazon 雲)上的中間層服務,以達到負載均衡和中間層服務故障轉移的目的。Spring Cloud 將 Eureka 集成在子項目 spring-cloud-netflix 中,以實現 Spring Cloud 的服務註冊和發現功能。

4.Spring Cloud Config

Spring Cloud Config 是 Spring Cloud 全家桶中最早的配置中心,雖然在生產場景中很多企業已經使用 Nacos 或者 Consul 整合型的配置中心替代了獨立的配置中心,但是 Config 依然適用於 Spring Cloud 項目,通過簡單地配置即可使用。

5.Zuul

Zuul 是 Netflix 開源網關,可以和 Eureka、Ribbon、Hystrix 等組件配合使用,Spring Cloud 對 Zuul 進行了整合與增強,使用它作爲微服務集羣的內部網關,負責給集羣內部的各個 Provider(服務提供者)提供 RPC 路由和對請求進行過濾。

6.Nginx/OpenResty

Nginx 是一個高性能 HTTP 和反向代理服務器,是由伊戈爾 · 賽索耶夫爲俄羅斯訪問量第二的 Rambler.ru 站點開發的 Web 服務器。Nginx 源代碼以類 BSD 許可證的形式對外發布,它的第一個公開版本 0.1.0 在 2004 年 10 月 4 日發佈,1.0.4 版本在 2011 年 6 月 1 日發佈。Nginx 因高穩定性、豐富的功能集、內存消耗少、併發能力強而聞名全球,並被廣泛使用,百度、京東、新浪、網易、騰訊、淘寶等都是它的用戶。OpenResty 是一個基於 Nginx 與 Lua 的高性能 Web 平臺,它的內部集成了大量精良的 Lua 庫、第三方模塊以及大多數的依賴項,用於快速搭建能夠處理超高併發的擴展性極高的動態 Web 應用、Web 服務和動態網關。

以上中間件的端口配置以及部分安裝與使用的演示視頻如表 1-2 所示。

表 1-2 本文案例涉及的主要中間件的端口配置以及部分安裝與使用的演示視頻

◆Spring Cloud 微服務開發和自驗證環境

在開始學習 Spring Cloud 核心編程之前,先來介紹一下開發和自驗證環境的準備、中間件的安裝以及抓包工具的準備。

開發和自驗證環境的系統選項和環境變量配置

首先介紹開發和自驗證系統的選型。大部分開發人員學習開發都用過 Windows 環境,在這種情況下,強烈建議使用虛擬機裝載 CentOS 作爲自驗證環境。爲什麼要推薦 CentOS 呢?

  1. 提前暴露生產環境中的問題

在生產環境上,90% 以上的 Java 應用都是使用 Linux 環境(如 CentOS)來部署的。因此,使用 CentOS 作爲自驗證環境可以提前暴露生產環境中的潛在問題,避免在開發時沒有發現有問題的程序,一旦部署到生產環境中就出現問題(筆者親歷)。

  1. 學習 Shell 命令和腳本

在生產環境中定位、分析、解決線上 Bug 時,需要用到基礎的 Shell 命令和腳本,因此平時要多使用、多練習。另外,Shell 命令和腳本是 Java 程序員必知必會的面試題。使用 CentOS 作爲自驗證環境能方便大家學習 Shell 命令和腳本。

當然,可以藉助一些文件同步或共享工具提高開發效率。比如,可以通過 VMware Tools 共享 Windows 和 CentOS 之間的文件夾,這樣在後續的 Lua 腳本的開發和調試過程中能避免來回地複製文件。

這裏給大家介紹一下 crazy-springcloud 腳手架開發和自驗證環境的準備,主要涉及兩個方面:

(1)中間件(含 Eureka、Redis、MySQL 等)相關信息的環境變量的配置。

(2)主機名稱的配置。

對於中間件相關信息(如 IP 地址、端口、用戶賬號等),很多項目都是直接以明文編碼的方式存放在配置文件中,這樣存在安全隱患甚至會引發泄密的風險。對於這些信息,建議通過操作系統環境變量進行配置,然後在配置文件中使用環境變量而不是明文編碼。

例如,可以對 Eureka 的 IP 提前配置好環境變量 EUREKA_ZONE_HOST,然後在應用的配置文件 bootstrap.yml 中按照如下方式來使用:

eureka:
 client:
 serviceUrl:
 defaultZone: ${SCAFFOLD_EUREKA_ZONE_HOSTS:http://localhost:7777/eureka/}

在上面的配置中,通過 ${
SCAFFOLD_EUREKA_ZONE_HOSTS} 表達式從環境變量中獲取 Eureka 的 service-url 地址。環境變量 SCAFFOLD_EUREKA_ZONE_HOSTS 後面跟着一個冒號和一個默認值,表示如果環境變量值爲空,就會使用默認值
http://localhost:7777/eureka/ 作爲配置項的值。

通過環境變量配置中間件的信息有什麼好處呢?

一是使配置信息的切換多了一層靈活性,如果切換 IP,那麼只需修改環境變量即可;二是可以不用在配置文件中以明文編碼方式存放密碼之類的敏感信息,多了一層安全性。

crazy-springcloud 微服務開發腳手架用到的環境變量較多,以自驗證環境 CentOS 中的配置文件 / etc/profile 爲例,部分內容大致如下:

export SCAFFOLD_DB_HOST=192.168.233.128
export SCAFFOLD_DB_USER=root
export SCAFFOLD_DB_PSW=root
export SCAFFOLD_REDIS_HOST=192.168.233.128
export SCAFFOLD_REDIS_PSW=123456
export SCAFFOLD_EUREKA_ZONE_HOSTS=http://192.168.233.128:7777/eureka/
export RABBITMQ_HOST=192.168.233.128
export SCAFFOLD_ZOOKEEPER_HOSTS=192.168.233.128:2181

以上環境變量中的 192.168.233.128 是筆者自驗證環境 CentOS 虛擬機的 IP 地址,Redis、ZooKeeper、Eureka、MySQL、Nginx 等中間件都運行在這臺虛擬機上,大家在運行 crazy-springcloud 微服務開發腳手架之前需要進行相應的更改。

最後介紹一下有關主機名稱的配置。如果在調試過程中直接通過 IP 訪問 REST 接口,那麼在 Fiddler 工具抓包中查看報文就不方便。爲了方便抓包,將 IP 地址都映射成主機名稱。在筆者使用的 Windows 開發環境中,hosts 文件內配置的主機名稱如下:

127.0.0.1 crazydemo.com
127.0.0.1 file.crazydemo.com
127.0.0.1 admin.crazydemo.com
127.0.0.1 xxx.crazydemo.com
192.168.233.128 eureka.server
192.168.233.128 zuul.server
192.168.233.128 nginx.server
192.168.233.128 admin.nginx.server

注意,本書後文的演示用例用到的 URL 會使用以上主機名稱取代 IP 地址。

使用 Fiddler 工具抓包和查看報文

在微服務程序開發和驗證的過程中,一般來說對 HTTP 接口發起請求有多種方式:

(1)直接發起請求。

(2)通過內部網關代理(如 Zuul)發起請求。

(3)通過外部網關反向代理(如 Nginx)發起請求。

以 crazy-springcloud 腳手架中的 uaa-provider 服務的 HTTP 接口 / api/user/detail/v1 爲例,通過以上 3 種方式發起請求的 HTTP 鏈路示意圖如圖 1-2 所示。

圖 1-2 3 種方式請求 uaa-provider 的 HTTP 鏈路示意圖

在生產環境下,爲了滿足內外網之間的轉發、多服務器之間的負載均衡要求,外部反向代理(Nginx)往往不止一層。因此,請求的 HTTP 鏈路往往更加複雜。

無論是在開發環境、自驗證環境、測試環境還是在生產環境中,查看 HTTP 接口的訪問鏈路和報文內容對於定位、分析、解決問題來說都非常重要,這就需要使用抓包工具。抓包工具的類型比較多,筆者目前使用較多的爲 Fiddler。

比如,在調試本書 crazy-springcloud 腳手架中的 uaa-provider 功能時,使用 Fiddler 能全面地查看發往服務端的 HTTP 報文的請求頭和響應頭,如圖 1-3 所示。

圖 1-3 使用 Fiddler 查看請求頭和響應頭

在開發過程中,Fiddler 這類抓包工具的使用對於分析和定位問題非常有用。筆者經常使用 Fiddler 完成下面的工作:

(1)查看 REST 接口的處理時間,在解決性能問題時幫助查看接口的整體時間。

(2)查看 REST 接口的請求頭、響應頭、響應內容,主要用於查看請求 URL、請求頭、響應頭是否正確,並且在必要的時候可以將所有請求頭一次性地複製到 Postman 等請求發起工具,幫助新請求快速地構造同樣的 HTTP 頭部。

(3)請求重發,除了可以使用獨立的請求工具(如 SwaggerUI/Postman 等)重發請求之外,還可以在 Fiddler 中直接進行請求重發,重發的請求有相同的頭部和參數,調試時非常方便。

◆crazy-springcloud 微服務開發腳手架

無論是單體應用還是分佈式應用,如果從零開始開發,那麼都會涉及很多基礎性的、重複性的工作,比如用戶認證、Session 管理等。

有了開發腳手架,這些基礎工作就可以省去,直接利用腳手架提供的基礎模塊,然後按照腳手架的規範進行業務模塊的開發即可。

筆者在開源平臺看到過不少開源的腳手架,但是發現這些腳手架很少可以直接拿來進行業務模塊的開發,要麼封裝過於重量級而不好解耦,要麼業務模塊分包不清晰而不方便開發,所以本着簡潔和清晰的原則,筆者發起的瘋狂創客圈社羣推出了自己的微服務開發腳手架 crazy-springcloud,它的模塊和功能如下:

crazymaker-server -- 根項目
│ ├─cloud-center -- 微服務的基礎設施中心
│ │ ├─cloud-eureka -- 註冊中心
│ │ ├─cloud-config -- 配置中心
│ │ ├─cloud-zuul -- 網關服務
│ │ ├─cloud-zipkin -- 監控中心
│ ├─crazymaker-base -- 公共基礎依賴模塊
│ │ ├─base-common -- 普通的公共依賴,如utils類的公共方法
│ │ ├─base-redis -- 公共的Redis操作模塊
│ │ ├─base-zookeeper -- 公共的ZooKeeper操作模塊
│ │ ├─base-session -- 分佈式Session模塊
│ │ ├─base-auth -- 基於JWT + SpringSecurity的用戶憑證與認證模塊
│ │ ├─base-runtime -- 各Provider的運行時公共依賴,裝配了一些通用Spring
 IOC Bean實例
│ ├─crazymaker-uaa -- 業務模塊: 用戶認證與授權
│ │ ├─uaa-api -- 用戶DTO、Constants等
│ │ ├─uaa-client -- 用戶服務的Feign遠程客戶端
│ │ ├─uaa-provider -- 用戶認證與權限的實現,包含controller層、service層、dao層的代碼實現
│ ├─crazymaker-seckill -- 業務模塊:秒殺練習
│ │ ├─seckill-api -- 秒殺DTO、Constants等
│ │ ├─seckill-client -- 秒殺服務的Feign遠程調用模塊
│ │ ├─seckill-provider -- 秒殺服務核心實現,包含controller層、service層、 dao層的代碼實現
│ ├─crazymaker-demo -- 業務模塊:練習演示
│ │ ├─demo-api -- 演示模塊的DTO、Constants等
│ │ ├─demo-client -- 演示模塊的Feign遠程調用模塊
│ │ ├─demo-provider -- 演示模塊的核心實現,包含controller層、service層、 dao層的代碼實現

在業務模塊如何分包的問題上,大部分企業都有自己的統一規範。crazy-springcloud 腳手架從職責清晰、方便維護、能快速導航代碼的角度出發,將每一個業務模塊細分成以下 3 個子模塊。

(1){module}-api:該子模塊定義了一些公共的 Constants 業務常量和 DTO 傳輸對象,既被業務模塊內部依賴,又可能被依賴該業務模塊的外部模塊所依賴。

(2){module}-client:該子模塊定義了一些被外部模塊所依賴的 Feign 遠程調用客戶類,是專供給外部模塊的依賴,不能被內部的其他子模塊所依賴。

(3){module}-provider:該子模塊是整個業務模塊的核心,也是一個能夠獨立啓動、運行的服務提供者(Application)。該模塊包含涉及業務邏輯的 controller 層、service 層、dao 層的完整代碼實現。

crazy-springcloud 微服務開發腳手架在以下兩方面進行了弱化:

(1)在部署方面對容器的介紹進行了弱化,沒有使用 Docker 容器而是使用 Shell 腳本。這有多方面的原因:一是本腳手架的目的是學習,使用 Shell 腳本而不是 Docker 去部署,方便大家學習 Shell 命令和腳本;二是 Java 和 Docker 其實整合得很好,學習起來非常容易,稍加配置就能做到一鍵發佈,找點資料學習一下就可以輕鬆掌握;三是部署和運維是一項專門的工作,生產環境的部署,甚至是整個自動化構建和部署的工作實際上是屬於運維的專項工作,由專門的運維人員去完成,而部署的核心仍然是 Shell 腳本,所以對於開發人員來說掌握 Shell 腳本纔是重中之重。

(2)對監控軟件的介紹進行了弱化。本書沒有專門介紹鏈路監控、JVM 性能指標、熔斷器監控軟件的使用,這也有多方面的原因:一是監控軟件太多,如果介紹得太全,篇幅就不夠,介紹得太少,大家又不一定會用到;二是監控軟件的使用大多是一些軟件的操作步驟和說明,原理性的內容比較少,傳播這類知識使用視頻的形式比文字的形式效果更好。瘋狂創客圈後續可能會推出一些微服務監控方面的教學視頻供大家參考,請大家關注社羣博客。無論如何,只要掌握了 Spring Cloud 的核心原理,那麼掌握監控組件的使用對大家來說基本上就是小菜一碟。

◆以秒殺作爲 Spring Cloud+Nginx 的實戰案例

本文的綜合性實戰案例是實現一個高性能的秒殺系統。爲何要以秒殺作爲本書的綜合性實戰案例呢?先回顧一下在單體架構還是主流的年代,大家學習 J2EE 技術時的綜合性實戰案例。一般來說,都是從 0 開始編寫代碼,一行一行地編寫一個購物車應用。通過編寫購物車應用能對 J2EE 有一個全方位的練習,包括前端的 HTML 網頁、JavaScript 腳本,後端的 MVC 框架、數據庫、事務、多線程等各種技術。

時代在變,技術的複雜度在變,前端和後端的分工也變了。現在的 J2EE 開發已經進入分佈式微服務架構的時代,前端和後端框架都變得非常複雜,前端和後端工程師已經有比較明確的分工。後端程序員專門做 Java 開發,前端程序員專門做前端的開發。後端程序員可以不需要懂前端的技術,如 Vue、TypeScript 等,當然,很多前端程序員也不一定需要懂後端技術。

相比單體服務時代,現在的分佈式開發時代學習 Java 後端技術的難度大多了。首先面臨一大堆分佈式、高性能中間件的學習,比如 Netty、ZooKeeper、RabbitMQ、Spring Cloud、Redis 等都是當今後端程序員必知必會的。然後像 JMeter 這類壓力測試工具和 Fiddler 這類抓包工具,已經成爲每個後端程序員必須掌握的知識。因爲在分佈式環境下需要定位、發現並解決數據一致性、高可靠性等問題,通過壓力測試,本來很正常的代碼也會在運行時出現很多性能相關的問題。

另外,隨着移動互聯網、物聯網的發展,當前面臨的高併發場景已經不侷限於電商,在其他的應用中也越來越多。所以,現在高併發開發技術由少數工程師需要掌握的高精尖技術變成了大多數人都需要掌握的基礎技能。一般來說,高併發開發的三大利器爲緩存、降級和限流。緩存的目的是提高系統訪問速度,它是對抗高併發的銀彈;而降級是當服務出問題或者服務影響到核心流程時,可以將服務暫時屏蔽掉,待高峯或者問題解決後再打開;而有些場景並不能用緩存和降級來解決,比如稀缺資源(秒殺、搶購)、寫數據(如評論、下單)等,這種情況下可以使用限流措施來對接口進行保護。

有了緩存、降級和限流這三大利器,遇到像京東 618、阿里雙 11 這樣的高併發應用場景,纔不用擔心瞬間流量導致系統雪崩,哪怕是最終只能做到有損的服務,也不會出現某些小電商平臺在活動期間服務器宕機數小時的事故。

秒殺程序的業務足夠簡單,涉及的技術又足夠全面,可以說是分佈式應用場景非常好的實戰案例。另外,現在 IT 行業人才流動性比較大,大家都會爲面試做準備。在面試中,秒殺業務所覆蓋的緩存、降級、高併發限流、分佈式鎖、分佈式 ID、數據一致性等問題一般是重點、熱門問題。

來源:

https://www.toutiao.com/i6955418439607419425/

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/eXGuSSG3tg9YSb12F7aPlQ