本系列示例與膠水代碼地址: https://github.com/HashZhang/spring-cloud-scaffold
Spring Cloud還是比較活躍的,更新一直很快。我一般考慮最新版本SR2發佈之後,再考慮升級(一般SR1還有SR2會有一些新老框架的兼容性升級)。而且由於需要我們線上穩定,結合我們的發佈週期來看,跳一個大版本升級是一個更好的選擇(也就是一年做一次大版本升級)。例如我們之前的升級路線就是:Brixton -> Daltson -> Finchley -> 當前的Hoxton
做了這麼多次升級,感覺可以出這個系列,來分享我們項目使用Spring cloud
框架實現的框架功能,在升級中遇到的坑,以及如何升級等等。每個版本都會有實例代碼,並在上一版本實現的功能基礎上,實現更多更實用的功能。所有示例代碼都在開頭提到的項目中,每個版本系列的最後,還會附上功能測試流程。
在Hoxton版本Release的同時,Spring Cloud也宣佈,其中的這些項目,已經進入維護模式(不再開發新功能),用戶最好做如下的替換:
- Spring Cloud Netflix Ribbon -> Spring Cloud Load Balancer
- Spring Cloud NetFlix Zuul -> Spring Cloud Gateway
- Spring Cloud Hystrix -> Spring Cloud Circuit Breaker + Resilience4J
- Spring Cloud Netflix Turbine -> Micrometer + Promethus
- Spring Cloud Netflix Archaius -> Spring Cloud Config Server
可以看出,Spring Cloud netflix中的zuul, ribbon, hystrix都基本上算是廢了,我們也可以拋棄掉Sprnig Cloud Netflix了。還有一個體系也在官方中,就是Spring Cloud Alibaba,但是通過Spring Cloud netflix這件事,我個人感覺這種依賴性質的膠水項目,最好還是我們架構組自己維護,這塊是比較容易有坑的,自己維護自己用更新起來更高效,而且不會有粘合的項目都不更新了替換起來要人命的代價。
Spring Cloud Hoxton,至少對於官方文檔來說,是一個里程碑式的變化。官方文檔終於將所有項目的文檔分開了,並且做了比較多的整理,可以看出,這個Hoxton一定是有人下定決心要做一個變革了。並且,Spring Cloud在這個版本引入了更多的虛擬化,雲原生依賴,例如Spring-Cloud-kubernetes,確實,有些服務發現,調用策略什麼的,Spring Cloud和k8s體系重複了,這個依賴可以使我們靈活地切換這些功能到底交給誰來做,期待這個項目的完善成熟。
這篇文章,會主要列出升級步驟與詳細說明,以及對應的源代碼,和實現的功能。以及如何替換Spring Cloud Netflix體系爲新的組件。
原有的功能以及之前的實現
1. 微服務
以前的體系:
- 註冊中心:Eureka
- 客戶端封裝:OpenFeign
- 客戶端負載均衡:Ribbon
- 斷路器與隔離: Hystrix
實現的功能:
- 所有集羣公用同一個公共Eureka集羣,集羣之間不互相調用,通過實例的
metamap
中的zone
配置,來區分不同集羣的實例。之前通過Ribbon
的配置ServerListFilter
實現,使用com.netflix.loadbalancer.ZoneAffinityServerListFilter
作爲ServerListFilter
,參考:Spring cloud實現FeignClient指定Zone調用 - 微服務之間調用,有重試,只對GET請求進行重試,連接超時,讀取超時還有 4xx 和 5xx 的狀態碼都會重試。這個之前是通過加入
spring-retry
重試通過ribbon配置實現的。參考:Spring Cloud Finchley OpenFeign的重試配置相關的坑 - 微服務調用有線程隔離,例如微服務1調用微服務2和微服務3,調用微服務2的線程和微服務3的線程不一樣。之前是通過
Hystrix
配置實現hystrix.threadpool.default.coreSize=50
- 實現了實例級別的熔斷,而不是微服務級別的。當調用微服務的兩個實例的時候,當一個實例一直異常,則將這個實例斷路器打開一段時間,而不是整個微服務都不能工作。之前通過
Ribbon
的配置LoadBalancerRule
實現,使用com.netflix.loadbalancer.AvailabilityFilteringRule
作爲LoadBalancerRule
。參考:Ribbon的AvailabilityFilteringRule的坑
2. 網關
以前的體系:
- API網關:Zuul
實現的功能:
- 重試,只對GET請求進行重試,連接超時,讀取超時還有 4xx 和 5xx 的狀態碼都會重試。這個之前是通過加入
spring-retry
重試通過ribbon配置實現的。 - 微服務調用有線程隔離,例如微服務1調用微服務2和微服務3,調用微服務2的線程和微服務3的線程不一樣。之前是通過
Hystrix
配置實現hystrix.threadpool.default.coreSize=50
- 實現了實例級別的熔斷,而不是微服務級別的。當調用微服務的兩個實例的時候,當一個實例一直異常,則將這個實例斷路器打開一段時間,而不是整個微服務都不能工作。之前通過
Ribbon
的配置LoadBalancerRule
實現,使用com.netflix.loadbalancer.AvailabilityFilteringRule
作爲LoadBalancerRule
。參考:Ribbon的AvailabilityFilteringRule的坑 - 特定接口 request body 解密與特定接口 response body 的加密。
3. Eureka-Server
實現的功能:
- 實例的快速上線下線,參考:Eureka 服務實例實現快速下線快速感知快速刷新配置解析
現在要實現的功能
1. 微服務
- 微服務之間調用依然基於利用 open-feign 的方式,有重試,僅對GET請求並且狀態碼爲4xx和5xx進行重試(對4xx重試是因爲滾動升級的時候,老的實例沒有新的 api,重試可以將請求發到新的實例上)
- 某個微服務調用其他的微服務 A 和微服務 B, 調用 A 和調用 B 的線程池不一樣。並且調用不同實例的線程池也不一樣。也就是實例級別的線程隔離
- 實現實例級別的熔斷。
- 使用 zone 隔離,不同 zone 之間不能互相調用
- 負載均衡的輪詢算法,需要請求與請求之間隔離,不能共用同一個 position 導致某個請求失敗之後的重試還是原來失敗的實例
2. 網關
- 轉發請求,有重試,僅對GET請求並且狀態碼爲4xx和5xx進行重試
- 不同微服務的不同實例線程隔離
- 實現實例級別的熔斷。
- 使用 zone 隔離,僅轉發請求到同 zone 的實例
- 負載均衡的輪詢算法,需要請求與請求之間隔離,不能共用同一個 position 導致某個請求失敗之後的重試還是原來失敗的實例
3. Eureka
- 實現服務實例快速上下線
新的pom依賴
1. 微服務
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.7.RELEASE</version>
</parent>
<properties>
<disruptor.version>3.4.2</disruptor.version>
<resilience4j-spring-cloud2.version>1.1.0</resilience4j-spring-cloud2.version>
</properties>
<dependencies>
<!--內部緩存框架統一採用caffeine-->
<!--這樣Spring cloud loadbalancer用的本地實例緩存也是基於Caffeine-->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<!--日誌需要用log4j2-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!--lombok簡化代碼-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--註冊到eureka-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--spring cloud rpc相關-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--調用路徑記錄-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<!--暴露actuator相關端口-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--暴露http接口, servlet框架採用nio的undertow,注意直接內存使用,減少GC-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-cloud2</artifactId>
<version>${resilience4j-spring-cloud2.version}</version>
</dependency>
<!--log4j2異步日誌需要的依賴,所有項目都必須用log4j2和異步日誌配置-->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>${disruptor.version}</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<!--最好用JDK 12版本及以上編譯,11.0.7對於spring-cloud-gateway有時候編譯會有bug-->
<!--雖然官網說已解決,但是11.0.7還是偶爾會出現-->
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</build>
2. 網關
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.7.RELEASE</version>
</parent>
<properties>
<disruptor.version>3.4.2</disruptor.version>
<resilience4j-spring-cloud2.version>1.1.0</resilience4j-spring-cloud2.version>
</properties>
<dependencies>
<!--內部緩存框架統一採用caffeine-->
<!--這樣Spring cloud loadbalancer用的本地實例緩存也是基於Caffeine-->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<!--日誌需要用log4j2-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!--lombok簡化代碼-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--調用路徑記錄-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<!--暴露actuator相關端口-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-cloud2</artifactId>
<version>${resilience4j-spring-cloud2.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
</dependency>
<!--log4j2異步日誌需要的依賴,所有項目都必須用log4j2和異步日誌配置-->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>${disruptor.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<!--最好用JDK 12版本及以上編譯,11.0.7對於spring-cloud-gateway有時候編譯會有bug-->
<!--雖然官網說已解決,但是11.0.7還是偶爾會出現-->
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</build>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
3. Eureka-Server
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.7.RELEASE</version>
</parent>
<properties>
<disruptor.version>3.4.2</disruptor.version>
</properties>
<dependencies>
<!--內部緩存框架統一採用caffeine-->
<!--這樣Spring cloud loadbalancer用的本地實例緩存也是基於Caffeine-->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<!--日誌需要用log4j2-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!--lombok簡化代碼-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!--調用路徑記錄-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>
<!--暴露actuator相關端口-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--暴露http接口, servlet框架採用nio的undertow,注意直接內存使用,減少GC-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<!--log4j2異步日誌需要的依賴,所有項目都必須用log4j2和異步日誌配置-->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>${disruptor.version}</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.6.1</version>
<configuration>
<!--最好用JDK 12版本及以上編譯,11.0.7對於spring-cloud-gateway有時候編譯會有bug-->
<!--雖然官網說已解決,但是11.0.7還是偶爾會出現-->
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</build>