前言
本篇文章主要介紹的是SpringCloud相關知識、微服務架構以及搭建服務注冊與發現的服務模塊(Eureka)以及Eureka集群。
GitHub源碼鏈接位于文章底部。
什么是SpringCloud
Spring Cloud 是一系列框架的有序集合。 它利用 Spring Boot 的開發便利性巧妙地簡化了分布式系統基礎設施的開發, 如服務發現注冊、配置中心、消息總線、負載均衡、熔斷器、數據監控等,都可以用 Spring Boot 的開發風格做到一鍵啟動和部署。Spring 并沒有復制造輪子,它只是將目前各家公司開發的比較成熟、經得起實際考驗的服務框架組合起來,通過 SpringBoot 風格進行再封裝屏蔽掉了復雜的配置和實現原理,最終開發者留出了一套簡單易懂、易部署和易維護的分布式系統開發工具包。
SpringCloud 與 SpringBoot 的關系
Spring Boot是Spring的一套快速配置腳手架,可以基于Spring Boot快速開發單個微服務,Spring Cloud是一個基于Spring Boot實現的云應用開發工具;Spring Boot專注于快速、方便集成的單個微服務個體,Spring Cloud關注全局的服務治理框架;Spring Boot 使用了默認大于配置的理念,很多集成方案已經幫你選擇好了,能不配置就不配置,Spring Cloud很大的一部分是基于Spring Boot來實現,可以不基于Spring Boot嗎?不可以。
Spring Boot可以離開Spring Cloud獨立使用開發項目, 但是Spring Cloud離不開Spring Boot,屬于依賴的關系。
SpringCloud 主要組件
用途 | 組件 |
---|---|
服務發現 | Netflix Eureka |
服務調用 | Netflix Feign |
熔斷器 | Netflix Hystrix |
服務網關 | Netflix Zuul |
分布式配置 | Spring Cloud Config |
消息總線 | Spring Cloud Bus |
SpringCloud 與 Dubbo 對比
dubbo由于是二進制的傳輸,占用帶寬會更少。
springCloud是http協議傳輸,帶寬會比較多,同時使用http協議一般會使用JSON報文,消耗會更大。
dubbo的開發難度較大,原因是dubbo的jar包依賴問題很多大型工程無法解決。
Dubbo只是實現了服務治理,而Spring Cloud下面有很多個子項目分別覆蓋了微服務架構下的方方面面,服務治理只是其中的一個方面,一定程度來說,Dubbo 只是 Spring CloudNetflix 中的一個子集。
Dubbo | SpringCloud | |
---|---|---|
服務注冊中心 | Zookeeper | Spring Cloud Netflix Eureka |
服務調用方式 | RPC | REST API |
服務網關 | 無 | Spring Cloud Netflix Zuul /Spring Cloud GateWay |
熔斷器 | 不完善 | Spring Cloud Netflix Hystrix |
分布式配置 | 無 | Spring Cloud Config |
服務跟蹤 | 無 | Spring Cloud Sleuth |
消息總線 | 無 | Spring Cloud Bus |
數據流 | 無 | Spring Cloud Stream |
批量任務 | 無 | Spring Cloud Task |
...... | ...... | ...... |
SpringCloud 的版本
SpringCloud 由于是一系列框架組合,為了避免與包含的自框架版本產生混淆,采用倫敦地鐵站的名稱作為版本名,形式為版本名+里程碑號。M9為第 9 個里程碑版本。以下是SpringBoot與Spring Cloud版本的對照表。
Spring Boot | Spring Cloud |
---|---|
1.2.x | Angel 版本 |
1.3.x | Brixton 版本 |
1.4.x | Camden 版本 |
1.5.x | Dalston 版本、 Edgware 版本 |
2.0.x | Finchley 版本 |
服務發現組件 Eureka
Eureka是Netflix 開發的服務發現框架,SpringCloud將它集成在自己的子項目spring-cloud-netflix中,實現SpringCloud的服務發現功能。Eureka包含兩個組件:Eureka Server和Eureka Client。
Eureka Server提供服務注冊服務,各個節點啟動后,會在Eureka Server中進行注冊,這樣EurekaServer中的服務注冊表中將會存儲所有可用服務節點的信息,服務節點的信息可以在界面中直觀的看到。
自我保護機制
Eureka Client是一個java客戶端,用于簡化與Eureka Server的交互,客戶端同時也就別一個內置的、使用輪詢(round-robin)負載算法的負載均衡器。在應用啟動后,將會向EurekaServer發送心跳,默認周期為30秒,如果Eureka Server在多個心跳周期內沒有接收到某個節點的心跳,Eureka Server將會從服務注冊表中把這個服務節點移除(默認90秒)。但是在短時間內丟失大量的實例心跳,這時候EurekaServer會開啟自我保護機制,Eureka不會踢出該服務,這就是Eureka的自我保護機制。
產生原因:在開發測試時,需要頻繁地重啟微服務實例,但是我們很少會把eureka server一起重啟(因為在開發過程中不會修改eureka注冊中心),當一分鐘內收到的心跳數大量減少時,會觸發該保護機制。可以在eureka管理界面看到Renews threshold和Renews(last min),當后者(最后一分鐘收到的心跳數)小于前者(心跳閾值)的時候,觸發保護機制,會出現紅色的警告:
EMERGENCY!EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT.RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEGING EXPIRED JUST TO BE SAFE.
從警告中可以看到,eureka認為雖然收不到實例的心跳,但它認為實例還是健康的,eureka會保護這些實例,不會把它們從注冊表中刪掉。
該保護機制的目的是避免網絡連接故障,在發生網絡故障時,微服務和注冊中心之間無法正常通信,但服務本身是健康的,不應該注銷該服務,如果eureka因網絡故障而把微服務誤刪了,那即使網絡恢復了,該微服務也不會重新注冊到eureka server了,因為只有在微服務啟動的時候才會發起注冊請求,后面只會發送心跳和服務列表請求,這樣的話,該實例雖然是運行著,但永遠不會被其它服務所感知。所以,eureka server在短時間內丟失過多的客戶端心跳時,會進入自我保護模式,該模式下,eureka會保護注冊表中的信息,不在注銷任何微服務,當網絡故障恢復后,eureka會自動退出保護模式。自我保護模式可以讓集群更加健壯。
但是我們在開發測試階段,需要頻繁地重啟發布,如果觸發了保護機制,則舊的服務實例沒有被刪除,這時請求有可能跑到舊的實例中,而該實例已經關閉了,這就導致請求錯誤,影響開發測試。所以,在開發測試階段,我們可以把自我保護模式關閉,只需在eureka server配置文件中加上如下配置即可:
#eureka基本配置信息
eureka:
client:
service-url:
#Eureka 客戶端與 Eureka 服務端進行交互的地址
defaultZone: http://127.0.0.1:${server.port}/eureka
#是否將自己注冊到Eureka服務中,本身就是注冊中心所以無需注冊
register-with-eureka: false
#是否從Eureka中檢索注冊信息,本身就是注冊中心所以無需檢索
fetch-registry: false
server:
# 測試時關閉自我保護機制,保證不可用服務及時踢出
enable-self-preservation: false
##剔除失效服務間隔
eviction-interval-timer-in-ms: 2000
在eureka client配置文件中加上:
#eureka基本配置信息
eureka:
client:
service-url:
#Eureka 客戶端與 Eureka 服務端進行交互的地址
defaultZone: http://127.0.0.1:8100/eureka/
# 心跳檢測檢測與續約時間
# 測試時將值設置設置小些,保證服務關閉后注冊中心能及時踢出服務
instance:
# Eureka客戶端向服務端發送心跳的時間間隔,單位為秒(客戶端告訴服務端自己會按照該規則)
lease-renewal-interval-in-seconds: 1
# Eureka服務端在收到最后一次心跳之后等待的時間上限,單位為秒,超過則剔除(客戶端告訴服務端按照此規則等待自己)
lease-expiration-duration-in-seconds: 2
但在生產環境,不會頻繁重啟,所以,一定要把自我保護機制打開,否則網絡一旦中斷,就無法恢復。
當然關于自我保護還有很多個性化配置,這里不詳細說明。
注意考慮網絡不可達情況下:調用接口冪等、重試、補償等。
創建工程
1. 目前工程結構
2. 首先創建父工程springcloud,以后這個工程下存放子工程eureka,feign,zuul等組件
將該工程的src文件夾刪除,在pom文件中添加依賴
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
<relativePath ></relativePath>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!--引用倉庫-->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<!--管理依賴,子項目中的依賴不用列出版本號-->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.M9</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
創建eureka父工程
新建springcloud-eureka項目,在pom文件中添加依賴
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
創建eureka服務端
以springcloud-eureka為父工程,新建springboot-eureka-server項目,這里不需要添加eureka-server依賴,因為父工程中有了。
1. 添加配置
在resources目錄中添加application.yml文件,在文件中添加配置
#服務端口號
server:
port: 8100
spring:
application:
name: eureka-server
#eureka基本配置信息
eureka:
client:
service-url:
#Eureka 客戶端與 Eureka 服務端進行交互的地址
defaultZone: http://127.0.0.1:${server.port}/eureka
#是否將自己注冊到Eureka服務中,本身就是注冊中心所以無需注冊
register-with-eureka: false
#是否從Eureka中檢索注冊信息,本身就是注冊中心所以無需檢索
fetch-registry: false
server:
# 測試時關閉自我保護機制,保證不可用服務及時踢出
enable-self-preservation: false
##剔除失效服務間隔
eviction-interval-timer-in-ms: 2000
2. 啟動類
java目錄下創建com.lxg二級目錄,然后創建EurekaServerApp啟動類
@SpringBootApplication
@EnableEurekaServer
public class EurekaServer {
public static void main(String[] args) {
SpringApplication.run(EurekaServer.class, args);
}
}
在類上加EnableEurekaServer注解,啟動EurekaServer。
創建eureka客戶端
以springcloud-eureka為父工程,新建springboot-eureka-client項目。在pom文件中添加依賴:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
1. 添加配置
在resources目錄中添加application.yml文件,在文件中添加配置
#端口號
server:
port: 9100
spring:
application:
name: eureka-client
#eureka基本配置信息
eureka:
client:
service-url:
#Eureka 客戶端與 Eureka 服務端進行交互的地址
defaultZone: http://127.0.0.1:8100/eureka/
# 心跳檢測檢測與續約時間
# 測試時將值設置設置小些,保證服務關閉后注冊中心能及時踢出服務
instance:
# Eureka客戶端向服務端發送心跳的時間間隔,單位為秒(客戶端告訴服務端自己會按照該規則)
lease-renewal-interval-in-seconds: 1
# Eureka服務端在收到最后一次心跳之后等待的時間上限,單位為秒,超過則剔除(客戶端告訴服務端按照此規則等待自己)
lease-expiration-duration-in-seconds: 2
2. 啟動類
java目錄下創建com.lxg二級目錄,然后創建EurekaClientApp啟動類,這里啟動類名稱不能為EurekaClient,否則會起沖突導致啟動失敗。類上使用EnableEurekaClient注解。
@SpringBootApplication
@EnableEurekaClient
public class EurekaClientApp {
public static void main(String[] args) {
SpringApplication.run(EurekaClientApp.class, args);
}
}
測試
先啟動服務端,再啟動客戶端,訪問127.0.0.1:8100 ,訪問監控頁面。
圖中的紅色字體是在提示已經關閉了eureka的自我保護機制,此時如果關閉客戶端,頁面中的服務就會被踢出,如果沒有關閉的話,即使客戶端關閉了,服務依然會存在。
高可用注冊中心(Eureka集群)
在微服務中,注冊中心非常核心,可以實現服務治理,如果一旦注冊出現故障的時候,可能會導致整個微服務無法訪問,在這時候就需要對注冊中心實現高可用集群模式。
Eureka高可用原理
默認情況下Eureka是讓服務注冊中心,不注冊自己,但是在集群中,需要設置能注冊自己,因為這兩個屬性默認為true,只需要不寫就行了。
###使該注冊中心注冊自己
register-with-eureka: true
###需要去注冊中心上檢索服務
fetch-registry: true
Eureka高可用實際上將自己作為服務向其他服務注冊中心注冊自己,這樣就可以形成一組相互注冊的服務注冊中心,從而實現服務清單的互相同步,達到高可用效果。
Eureka集群環境搭建
新增server1,server2,server3三個節點。
server1配置:
#服務端口號
server:
port: 8100
spring:
application:
name: eureka-server
#eureka基本配置信息
eureka:
client:
service-url:
#Eureka 客戶端與 Eureka 服務端進行交互的地址
defaultZone: http://127.0.0.1:8200/eureka,http://127.0.0.1:8300/eureka
server:
# 測試時關閉自我保護機制,保證不可用服務及時踢出
enable-self-preservation: false
##剔除失效服務間隔
eviction-interval-timer-in-ms: 2000
server2配置:
#服務端口號
server:
port: 8200
spring:
application:
name: eureka-server
#eureka基本配置信息
eureka:
client:
service-url:
#Eureka 客戶端與 Eureka 服務端進行交互的地址
defaultZone: http://127.0.0.1:8100/eureka,http://127.0.0.1:8300/eureka
server:
# 測試時關閉自我保護機制,保證不可用服務及時踢出
enable-self-preservation: false
##剔除失效服務間隔
eviction-interval-timer-in-ms: 2000
server3配置:
#服務端口號
server:
port: 8300
spring:
application:
name: eureka-server
#eureka基本配置信息
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:8100/eureka,http://127.0.0.1:8200/eureka
server:
# 測試時關閉自我保護機制,保證不可用服務及時踢出
enable-self-preservation: false
##剔除失效服務間隔
eviction-interval-timer-in-ms: 2000
然后修改客戶端的配置,因為以前是單個eureka注冊中心,只需要注冊進一個地址就行了,現在要注冊進所有的注冊中心。
#端口號
server:
port: 9100
spring:
application:
name: eureka-client
#eureka基本配置信息
eureka:
client:
service-url:
#Eureka 客戶端與 Eureka 服務端進行交互的地址
#單機
#defaultZone: http://127.0.0.1:8100/eureka/
#集群
defaultZone: http://127.0.0.1:8100/eureka/,http://127.0.0.1:8200/eureka/,http://127.0.0.1:8300/eureka/
# 心跳檢測檢測與續約時間
# 測試時將值設置設置小些,保證服務關閉后注冊中心能及時踢出服務
instance:
# Eureka客戶端向服務端發送心跳的時間間隔,單位為秒(客戶端告訴服務端自己會按照該規則)
lease-renewal-interval-in-seconds: 1
# Eureka服務端在收到最后一次心跳之后等待的時間上限,單位為秒,超過則剔除(客戶端告訴服務端按照此規則等待自己)
lease-expiration-duration-in-seconds: 2
先啟動所有的服務端節點,再啟動客戶端。訪問localhost:8100 ,localhost:8200 ,localhost:8300 都能進入eureka的界面,同時看到客戶端服務和其他的注冊中心。這個時候即使有某一個節點掛了,服務依然是可用的,而且性能肯定比單機版要好。
搭建eureka集群有幾點需要注意的:
1.與先前獨立運行注冊不同,注意defaultZone屬性,它的值為除了自己以外的所有eureka節點的地址,以英文逗號分割。
2.去掉fetch-registry 與 register-with-eureka配置(其實這樣做就會取對應的默認值,兩個值均為true),需要讓自己能被注冊和檢索。
3.啟動第一個注冊中心時會報Cannot execute request on any known server的錯誤,暫時不管它,實際上eureka注冊中心的ui界面是能打開的,當所有的節點啟動完畢,就能找到服務,此錯誤就會消失。
4.所有注冊中心的節點的spring.application.name必須保持一致。
5.當客戶端需要往注冊中心集群注冊服務時defaultZone屬性需要把所有節點地址都加上,如果像單節點一樣的話,你連接的那個節點掛了,集群中其他節點就無法獲取到該服務,也就不能達到高可用。
本文GitHub源碼:https://github.com/lixianguo5097/springcloud/tree/master/springcloud-eureka
CSDN:https://blog.csdn.net/qq_27682773
簡書:http://www.lxweimin.com/u/e99381e6886e
博客園:https://www.cnblogs.com/lixianguo