1. 程式人生 > >SpringCloud入門 ――服務註冊與發現(Eureka)

SpringCloud入門 ――服務註冊與發現(Eureka)

 

前言

Oracle轉讓Java,各種動態語言的曝光率上升,Java工程師的未來在哪裡?我覺得Spring Cloud讓未來有無限可能。拖了半年之久的Spring Cloud學習就從今天開始了。中文教材不多,而且大多都是簡單的離散的資訊,想要找到企業級的一體化解決方案很少。不過,對於入門來說,簡單就夠了,等到用的時候自然而然的彙總起來。

目標是把springcloud的子專案過一遍。

Component Edgware.SR2 Finchley.M7 Finchley.BUILD-SNAPSHOT
spring-cloud-aws 1.2.2.RELEASE 2.0.0.M4 2.0.0.BUILD-SNAPSHOT
spring-cloud-bus 1.3.2.RELEASE 2.0.0.M6 2.0.0.BUILD-SNAPSHOT
spring-cloud-cli 1.4.1.RELEASE 2.0.0.M1 2.0.0.BUILD-SNAPSHOT
spring-cloud-commons 1.3.2.RELEASE 2.0.0.M7 2.0.0.BUILD-SNAPSHOT
spring-cloud-contract 1.2.3.RELEASE 2.0.0.M7 2.0.0.BUILD-SNAPSHOT
spring-cloud-config 1.4.2.RELEASE 2.0.0.M7 2.0.0.BUILD-SNAPSHOT
spring-cloud-netflix 1.4.3.RELEASE 2.0.0.M7 2.0.0.BUILD-SNAPSHOT
spring-cloud-security 1.2.2.RELEASE 2.0.0.M2 2.0.0.BUILD-SNAPSHOT
spring-cloud-cloudfoundry 1.1.1.RELEASE 2.0.0.M3 2.0.0.BUILD-SNAPSHOT
spring-cloud-consul 1.3.2.RELEASE 2.0.0.M6 2.0.0.BUILD-SNAPSHOT
spring-cloud-sleuth 1.3.2.RELEASE 2.0.0.M7 2.0.0.BUILD-SNAPSHOT
spring-cloud-stream Ditmars.SR3 Elmhurst.RC1 Elmhurst.BUILD-SNAPSHOT
spring-cloud-zookeeper 1.2.0.RELEASE 2.0.0.M6 2.0.0.BUILD-SNAPSHOT
spring-boot 1.5.10.RELEASE 2.0.0.RC2 2.0.0.BUILD-SNAPSHOT
spring-cloud-task 1.2.2.RELEASE 2.0.0.M3 2.0.0.RELEASE
spring-cloud-vault 1.1.0.RELEASE 2.0.0.M6 2.0.0.BUILD-SNAPSHOT
spring-cloud-gateway 1.0.1.RELEASE 2.0.0.M7 2.0.0.BUILD-SNAPSHOT
spring-cloud-openfeign   2.0.0.M1 2.0.0.BUILD-SNAPSHOT

本次學習服務註冊與發現, Eureka。

Eureka介紹

Eureka是一個基於REST(Representational State Transfer)的服務,主要用於AWS cloud, 提供服務定位(locating services)、負載均衡(load balancing)、故障轉移(failover of middle-tier servers)。我們把它叫做Eureka Server. Eureka也提供了基於Java的客戶端元件,Eureka Client,內建的負載均衡器可以實現基本的round-robin負載均衡能力。在Netflix,一個基於Eureka的更復雜的負載均衡器針對多種因素(如流量、資源利用率、錯誤狀態等)提供加權負載均衡,以實現高可用(superior resiliency).

為什麼需要Eureka

在AWS Cloud,由於其天生的特性,伺服器經常變換。我們知道每個EC2掛掉後,重啟又是一個新的。不像傳統的固定IP,AWS的伺服器是變化的。因此需要更復雜的負載均衡方案來動態註冊和登出。由於AWS並沒有提供中間層負載均衡解決方案,Eureka填補了這個領域的巨大空白。

Eureka和AWS ELB有什麼不同

AWS ELB(Elastic Load Balancer)是面向終端使用者Web流量的邊緣服務的負載均衡解決方案。Eureka填補了對中間層負載均衡的需求。理論上,你可以把中間層服務放在AWS ELB之後,但在EC2模型中,你將會把他們直接暴露到外網,從而失去了AWS security groups的好處。(這裡有疑問,我現實使用的時候ELB也有區分VPC的,所以不會暴露到外網,不知道是不是本文釋出的時候AWS還沒這功能,所以感覺Eureka和ELB區別不大啊)。

AWS ELB也是一種傳統的基於代理的負載平衡解決方案,而Eureka則不同之處在於負載平衡發生在例項/伺服器/主機級別。客戶端例項知道他們需要與哪些伺服器互動的所有資訊。這樣的好壞取決於你怎麼看待它。如果你想要AWS現在提供的基於粘滯使用者session的負載均衡,Eureka沒有開箱即用的解決方案。在Netflix,我們更喜歡我們的服務是無狀態的(非粘性)。這有利於提供更好的擴充套件性,Eureka非常適合解決這個問題。(感覺這段也是吹水,現在的web互動大都是無狀態的,狀態通過redis,message queue等第三方維護,ELB照樣可以提供)。

使用Eureka區分基於代理的負載平衡和負載平衡的另一個重要方面是,您的應用程式可以靈活地處理負載平衡器的中斷,因為有關可用伺服器的資訊會快取在客戶端上。這確實需要少量的記憶體,但換得更好的彈性。

Eureka和Route 53有什麼不同

Route 53是一個域名服務,就像Eureka可以為中層伺服器提供相同的服務一樣,但僅此而已。 Route 53是一項DNS服務,即使對於非AWS資料中心,也可以託管您的DNS記錄。 Route 53還可以在AWS區域間執行基於延遲的路由。Eureka類似於內部DNS,與全世界的DNS伺服器無關。Eureka也是區域隔離的,因為它不知道其他AWS區域中的伺服器。儲存資訊的主要目的是在區域內進行負載平衡。

雖然你可以在Route 53中註冊你的中間層伺服器,並依賴AWS安全組保護你的伺服器不受外網訪問,但你的中間層伺服器身份仍然暴露於外網環境。它同樣帶有傳統基於DNS的負載均衡方案的缺點,其中流量仍然會被路由到已經不健康或已經不存在的伺服器上(在AWS雲中,伺服器隨時可能消失)。

Eureka如何使用?

在Netflix,Eureka不僅是中間層負載均衡關鍵部分,還有以下功能:

與Netflix Asgard一起提供紅/黑部署服務, Asgard是一個讓雲部署更方便的開源服務。Eureka會與Asgard搭配,讓應用在新/老版本部署切換,讓故障處理更快速和無縫,尤其是當啟動100個例項部署時要花費很長時間的時候。

當我們的cassandra需要維護時,停止Cassandra例項。

為我們的memcached快取服務提供識別環上例項列表功能。

為特定的應用提供因意外導致故障儲存元資訊的服務。

Eureka使用時機?

當你的服務執行在AWS雲上並且你不希望使用AWS ELB註冊或暴露給外網。你要麼需要使用類似round-robin這種簡單的負載均衡方案或者想要寫一個基於Eureka包裝過的符合要求的負載均衡器。你沒有session粘性,沒有session繫結機制和在外部快取(例如 memcached)載入會話資料的需要。更重要的是,如果你的架構風格適合一個基於客戶端的負載均衡模型,Eureka相當適合這個場景。

應用客戶端和應用服務端如何通訊?

通訊技術可以是任何你喜歡的。Eureka幫你找到你需要通訊的服務資訊但沒有引入任何通訊協議或方法的限制。比如,你可以用Eureka獲取目標伺服器地址並使用thrift,http(s)或其他RPC機制的協議。

Eureka架構


上面的架構圖描述了Eureka是如何在Netflix部署的,這也是Eureka叢集的執行方式。在每個區域(region)都有一個eureka叢集,它只知道該區域內的例項資訊。每個分割槽(zone)至少有一個eureka伺服器來處理本分割槽故障。

服務註冊在Eureka上並且每30秒傳送心跳來續租。如果一個客戶端在幾次內沒有重新整理心跳,它將在大約90秒內被移出伺服器登錄檔。註冊資訊和更新資訊會在整個eureka叢集的節點進行復制。任何分割槽的客戶端都可查詢註冊中心資訊(每30秒發生一次)來定位他們的服務(可能會在任何分割槽)並進行遠端呼叫。

非Java服務和客戶端

對於非Java的服務,你可以用其他語言實現eureka的客戶端部分。基於REST的服務也暴露給了所有操作給Eureka客戶端。非Java客戶端也可以使用REST服務來查詢其他服務的資訊。

可配置

有了Eureka,你可以動態新增刪除叢集節點。你可以調整內部配置,從超時到執行緒池。Eureka使用archaius並且如果你有一個配置源的實現,那麼很多配置可以動態調優。

彈性

在AWS雲中,構建彈性伸縮必不可少。Eureka是我們經驗的結晶,並且在客戶端和服務端都內建了彈效能力。

Eureka客戶端設計成可以處理一個或多個Eureka服務端的失敗場景。由於Eureka客戶端有登錄檔快取資訊,即使所有的eureka伺服器都掛了,服務也能正常執行。

Eureka伺服器對於其他eureka節點掛了也提供了足夠的彈性。即使服務端和客戶端之間產生了網路分割槽,伺服器也由內建的彈性策略來防止大規模的停機。

多區域

在多個AWS區域部署Eureka是一個很簡單的工作。不同區域之間Eureka叢集並不通訊。

監控

Eureka使用servo來跟蹤服務端和客戶端的資訊,包括效能,監控和報警。資料儲存在JMX中並暴露給Amazon Cloud Watch。


Eureka服務治理體系

以下參考《Spring Cloud Eureka詳解》, 作者 大道化簡

大概讀完Eureka的簡介,應該可以知道Eureka是負責微服務架構中服務治理的功能,負責各個微服務例項的自動註冊和發現。

盜圖一張, 畫的很好。

服務註冊

在服務治理框架中,通常都會構建一個註冊中心,每個服務單元向註冊中心登記自己提供的服務,包括服務的主機與埠號、服務版本號、通訊協議等一些附加資訊。註冊中心按照服務名分類組織服務清單,同時還需要以心跳檢測的方式去監測清單中的服務是否可用,若不可用需要從服務清單中剔除,以達到排除故障服務的效果。

服務發現

在服務治理框架下,服務間的呼叫不再通過指定具體的例項地址來實現,而是通過服務名發起請求呼叫實現。服務呼叫方通過服務名從服務註冊中心的服務清單中獲取服務例項的列表清單,通過指定的負載均衡策略取出一個服務例項位置來進行服務呼叫。

Eureka服務端

Eureka服務端,即服務註冊中心。它同其他服務註冊中心一樣,支援高可用配置。依託於強一致性提供良好的服務例項可用性,可以應對多種不同的故障場景。

Eureka服務端支援叢集模式部署,當叢集中有分片發生故障的時候,Eureka會自動轉入自我保護模式。它允許在分片發生故障的時候繼續提供服務的發現和註冊,當故障分配恢復時,叢集中的其他分片會把他們的狀態再次同步回來。叢集中的的不同服務註冊中心通過非同步模式互相複製各自的狀態,這也意味著在給定的時間點每個例項關於所有服務的狀態可能存在不一致的現象。

Eureka客戶端

Eureka客戶端,主要處理服務的註冊和發現。客戶端服務通過註冊和引數配置的方式,嵌入在客戶端應用程式的程式碼中。在應用程式啟動時,Eureka客戶端向服務註冊中心註冊自身提供的服務,並週期性的傳送心跳來更新它的服務租約。同時,他也能從服務端查詢當前註冊的服務資訊並把它們快取到本地並週期行的重新整理服務狀態。


註冊中心

在服務治理框架中,通常都會構建一個註冊中心,每個服務單元向註冊中心登記自己提供的服務,包括服務的主機與埠號、服務版本號、通訊協議等一些附加資訊。註冊中心按照服務名分類組織服務清單,同時還需要以心跳檢測的方式去監測清單中的服務是否可用,若不可用需要從服務清單中剔除,以達到排除故障服務的效果。

建立Eureka Server

測試程式碼: https://github.com/Ryan-Miao/eureka-server

Eureka Server是基於springboot的,只要啟動一個springboot就可以了。start.spring.io提供了一系列啟動模板,而且Spring又和Idea比較曖昧,所以使用Idea可以超級簡單的搭建和整合Spring專案。

在Idea裡,新建專案,選擇Spring initializer.

然後,勾選你想要的元件就行了。這裡搜尋Eureka Server, 選擇

當然,建立好專案後記得先修改編碼為UTF8, 不然萬惡的GBK…

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

	<groupId>com.test</groupId>
	<artifactId>eureka-server</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>eureka-server</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.10.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
		<spring-cloud.version>Edgware.SR2</spring-cloud.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka-server</artifactId>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>


</project>

 

然後,在application.properties中加入配置資訊:

spring.application.name=eureka-server

#服務註冊中心埠號
server.port=1110

#服務註冊中心例項的主機名
eureka.instance.hostname=localhost

#是否向服務註冊中心註冊自己
eureka.client.register-with-eureka=false

#是否檢索服務
eureka.client.fetch-registry=false

#服務註冊中心的配置內容,指定服務註冊中心的位置
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/

 

修改啟動類,新增@EnableEurekaServer

@EnableEurekaServer
@SpringBootApplication
public class EurekaServerApplication {

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

然後,啟動main方法即可。如果埠有衝突,修改合適的埠重啟。demo中埠為1110,啟動後,訪問http://localhost:1110/ 可以看到狀態控制檯。

前文也說了,上述demo是註冊中心,所有的微服務要向本server註冊以實現負載均衡。那麼,首先就要保證註冊中心的穩定,於是就必須搭建Eureka叢集的高可用方案。

高可用服務註冊中心

考慮到發生故障的情況,服務註冊中心發生故障必將會造成整個系統的癱瘓,因此需要保證服務註冊中心的高可用。

Eureka Server在設計的時候就考慮了高可用設計,在Eureka服務治理設計中,所有節點既是服務的提供方,也是服務的消費方,服務註冊中心也不例外。

Eureka Server的高可用實際上就是將自己做為服務向其他服務註冊中心註冊自己,這樣就可以形成一組互相註冊的服務註冊中心,以實現服務清單的互相同步,達到高可用的效果。

構建服務註冊中心叢集

Eureka Server的同步遵循著一個非常簡單的原則:只要有一條邊將節點連線,就可以進行資訊傳播與同步。可以採用兩兩註冊的方式實現叢集中節點完全對等的效果,實現最高可用性叢集,任何一臺註冊中心故障都不會影響服務的註冊與發現.

所以,下面建立3個Eureka Server兩兩互相註冊,形成叢集。由於核心程式碼一樣,我們只要將其部署在不同的機器上即可。因此,我需要3個不同的配置檔案。

為了本地模擬,修改host,虛擬3個域名

windows host 位置C:\Windows\System32\drivers\etc

127.0.0.1 master
127.0.0.1 backup1
127.0.0.1 backup2

然後,給我們的Eureka Server增加3個配置檔案。此時,應該將application.properties裡除了spring.application.name之外的配置註釋掉,我們後面3個配置暫時不用上面幾個開關。

application-peer1.properties

server.port=1111

eureka.instance.hostname=master

eureka.client.serviceUrl.defaultZone=http://backup1:1112/eureka/,http://backup2:1113/eureka/

 

application-peer2.properties

server.port=1112

eureka.instance.hostname=backup1

eureka.client.serviceUrl.defaultZone=http://master:1111/eureka/,http://backup2:1113/eureka/

 

application-peer3.properties

server.port=1113

eureka.instance.hostname=backup2


eureka.client.serviceUrl.defaultZone=http://master:1111/eureka/,http://backup1:1112/eureka/

 

由於是本地開發環境,我們直接以maven啟動。當然,也可以選擇jar啟動。

分別開啟3個命令列

mvn spring-boot:run -Dspring.profiles.active=peer1
mvn spring-boot:run -Dspring.profiles.active=peer2
mvn spring-boot:run -Dspring.profiles.active=peer3

 

注意,由於啟動的時候會去指定zone註冊,而另外的server還沒啟動,這時候會報錯,Cannot execute request on any known server, 不用理會,接著啟動後兩個即可。

全部啟動成功後,訪問http://master:1111/

可以看到2個備份,線上的server有3個Availability Zones。Availability Zones在AWS中是指可用區,是在不同region裡的不同機房。

註冊中心高可用叢集demo搭建完畢。

失效剔除

有些時候,我們的服務例項並不一定會正常下線,可能由於記憶體溢位、網路故障等原因使服務不能正常運作。而服務註冊中心並未收到“服務下線”的請求,為了從服務列表中將這些無法提供服務的例項剔除,Eureka Server在啟動的時候會建立一個定時任務,預設每隔一段時間(預設為60秒)將當前清單中超時(預設為90秒)沒有續約的服務剔除出去。

自我保護

服務註冊到Eureka Server後,會維護一個心跳連線,告訴Eureka Server自己還活著。Eureka Server在執行期間會統計心跳失敗的比例在15分鐘以之內是否低於85%,如果出現低於的情況,Eureka Server會將當前例項註冊資訊保護起來,讓這些例項不會過期。這樣做會使客戶端很容易拿到實際已經不存在的服務例項,會出現呼叫失敗的情況。因此客戶端要有容錯機制,比如請求重試、斷路器。

以下是自我保護相關的屬性:

eureka.server.enableSelfPreservation=true. 可以設定改引數值為false,以確保註冊中心將不可用的例項刪除

region(地域)與zone(可用區)

region和zone(或者Availability Zone)均是AWS的概念。在非AWS環境下,我們可以簡單地將region理解為地域,zone理解成機房。一個region可以包含多個zone,可以理解為一個地域內的多個不同的機房。不同地域的距離很遠,一個地域的不同zone間距離往往較近,也可能在同一個機房內。

region可以通過配置檔案進行配置,如果不配置,會預設使用us-east-1。同樣Zone也可以配置,如果不配置,會預設使用defaultZone。

Eureka Server通過eureka.client.serviceUrl.defaultZone屬性設定Eureka的服務註冊中心的位置。

指定region和zone的屬性如下:

(1)eureka.client.availabilityZones.myregion=myzone# myregion是region

(2)eureka.client.region=myregion

Ribbon的預設策略會優先訪問通客戶端處於同一個region中的服務端例項,只有當同一個zone中沒有可用服務端例項的時候才會訪問其他zone中的例項。所以通過zone屬性的定義,配合實際部署的物理結構,我們就可以設計出應對區域性故障的容錯叢集。

安全驗證

剛才的demo中,我們註冊中心的面板是公開訪問的。這裡可以簡單加入使用者名稱密碼,讓訪問更安全。當然,你可以自己實現sso。
pom新增

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>

 

然後,為了簡單演示,事實上也應該這樣。我們將3個server的使用者名稱密碼設定一致。即,在application.properties裡新增:

security.user.name=admin
security.user.password=123456

 

然後,在我們其他三個配置檔案中的eureka.client.serviceUrl.defaultZone新增自己的url並加入使用者名稱密碼,
以peer1為例子

eureka.client.serviceUrl.defaultZone=http://${security.user.name}:${security.user.password}@${eureka.instance.hostname}:${server.port}/eureka/,http://backup1:1112/eureka/,http://backup2:1113/eureka/

 


服務提供者 微服務叢集

服務註冊

服務提供者在啟動的時候會通過REST請求的方式將自己註冊到Eureka Server上,同時帶上自身服務的一些元資料資訊。Eureka Server接收到這個Rest請求之後,將元資料資訊儲存在一個雙層結構的Map中,其中第一層的key是服務名。第二層的key 是具體服務的例項名。

在服務註冊時,需要確認一下eureka.client.register-with-eureka=true引數是否正確,該值預設為true。若設定為fasle將不會啟動註冊操作。

服務同步

從eureka服務治理體系架構圖中可以看到,不同的服務提供者可以註冊在不同的服務註冊中心上,它們的資訊被不同的服務註冊中心維護。

此時,由於多個服務註冊中心互相註冊為服務,當服務提供者傳送註冊請求到一個服務註冊中心時,它會將該請求轉發給叢集中相連的其他註冊中心,從而實現服務註冊中心之間的服務同步。通過服務同步,提供者的服務資訊就可以通過叢集中的任意一個服務註冊中心獲得。

服務續約

在註冊服務之後,服務提供者會維護一個心跳用來持續高速Eureka Server,“我還在持續提供服務”,否則Eureka Server的剔除任務會將該服務例項從服務列表中排除出去。我們稱之為服務續約。

下面是服務續約的兩個重要屬性:

(1)eureka.instance.lease-expiration-duration-in-seconds

leaseExpirationDurationInSeconds,表示eureka server至上一次收到client的心跳之後,等待下一次心跳的超時時間,在這個時間內若沒收到下一次心跳,則將移除該instance。

預設為90秒

如果該值太大,則很可能將流量轉發過去的時候,該instance已經不存活了。
如果該值設定太小了,則instance則很可能因為臨時的網路抖動而被摘除掉。
該值至少應該大於leaseRenewalIntervalInSeconds

(2)eureka.instance.lease-renewal-interval-in-seconds

leaseRenewalIntervalInSeconds,表示eureka client傳送心跳給server端的頻率。如果在leaseExpirationDurationInSeconds後,server端沒有收到client的心跳,則將摘除該instance。除此之外,如果該instance實現了HealthCheckCallback,並決定讓自己unavailable的話,則該instance也不會接收到流量。

預設30秒

建立並註冊服務提供者 Eureka Client

專案程式碼: https://github.com/Ryan-Miao/eureka-client

Eureka Server是註冊中心,我們的客戶端也要整合Eureka client來自我註冊。我們client專案也是基於Springboot的。同樣建立一個新的專案 eureka-client. 這次,要引入Eureka Discovery以及健康檢查Actuator。最終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">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.test</groupId>
	<artifactId>eureka-client</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>eureka-client</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.10.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
		<spring-cloud.version>Edgware.SR2</spring-cloud.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>


</project>

然後,啟動類新增@EnableDiscoveryClient

@EnableDiscoveryClient
@SpringBootApplication
public class EurekaClientApplication {

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

 

最後,新增配置資訊:

spring.application.name=eureka-client-service-provider
server.port=2001
eureka.client.serviceUrl.defaultZone=http://admin:[email protected]:1111/eureka/,http://admin:[email protected]:1112/eureka/,http://admin:[email protected]:1113/eureka/

這裡,我們把三個註冊中心的地址都加上。然後,為保證高可用,我們的服務提供者也需要叢集部署。真實生產環境中肯定是部署到不同的機器上,在本地模擬的話,我們只好以不同埠來模擬了。

啟動埠2001

mvn spring-boot:run -Dserver.port=2001

 

啟動埠2002

mvn spring-boot:run -Dserver.port=2002

 

然後,檢視註冊中心面板。


服務消費者 另一個微服務叢集

獲取服務

消費者服務啟動時,會發送一個Rest請求給服務註冊中心,來獲取上面註冊的服務清單。為了效能考慮,Eureka Server會維護一份只讀的服務註冊清單來返回給客戶端,同時該快取清單預設會每隔30秒更新一次。

下面是獲取服務的兩個重要的屬性:

(1) eureka.client.fetch-registry

是否需要去檢索尋找服務,預設是true

(2)eureka.client.registry-fetch-interval-seconds

表示eureka client間隔多久去拉取服務註冊資訊,預設為30秒,對於api-gateway,如果要迅速獲取服務註冊狀態,可以縮小該值,比如5秒

服務呼叫

服務消費者在獲取服務清單後,通過服務名可以獲取具體提供服務的例項名和該例項的元資料資訊。因為有這些服務例項的詳細資訊,所以客戶端可以根據自己的需要決定具體呼叫哪個例項,在Ribbon中會預設採用輪詢的方式進行呼叫,從而實現客戶端的負載均衡。

等學到Ribbon之後再繼續服務消費。

服務下線

在系統執行過程中必然會面臨關閉或重啟服務的某個例項的情況,在服務關閉操作時,會觸發一個服務下線的Rest服務請求給Eureka Server,告訴服務註冊中心:“我要下線了。”服務端在接收到該請求後,將該服務狀態置位下線(DOWN),並把該下線事件傳播出去。

配置詳解

服務例項類配置

端點配置

eureka例項的狀態頁面和健康監控的url預設為spring boot actuator提供的/info端點和/health端點。我們必須確保Eureka客戶端的/health端點在傳送元資料的時候,是一個能夠被註冊中心訪問到的地址,否則服務註冊中心不會根據應用的健康檢查來更改狀態(僅當開啟了healthcheck功能時,以該端點資訊作為健康檢查標準)。而如果/info端點不正確的話,會導致在Eureka面板中單擊服務時,無法訪問到服務例項提供的資訊介面。

大多數情況下,我們不需要修改這個幾個url配置。但是當應用不使用預設的上下文(context path或servlet path,比如配置server.servletPath=/test),或者管理終端路徑(比如配置management.contextPath=/admin)時,我們需要修改健康檢查和狀態頁的url地址資訊。

application.yml配置檔案如下:

server.context-path=/helloeureka

//下面配置為相對路徑,也支援配置成絕對路徑,例如需要支援https

eureka.instance.health-check-url-path=${server.context-path}/health

eureka.instance.status-page-url-path=${server.context-path}/info

元資料

元資料是Eureka客戶端在向服務註冊中心傳送註冊請求時,用來描述自身服務資訊的物件,其中包含了一些標準化的元資料,比如服務名稱、例項名稱、例項IP、例項埠等用於服務治理的重要資訊;以及一些用於負載均衡策略或是其他特殊用途的自定義元資料資訊。

我們可以通過eureka.instance.<properties>=<value>的格式對標準化元資料直接進行配置,其中<properties>就是EurekaInstanceConfigBean物件中的成員變數。而對於自定義元資料,可以通過eureka.instance.metadataMap.<key>=<value>的格式來進行配置。比如:

eureka.instance.metadataMap.zone=tianjin

//隨機生成例項名

eureka.instance.metadataMap.instanceId=${spring.application.name}:${random.value}

健康檢測

預設情況下,Eureka中各個服務例項的健康檢測並不是通過spring-boot-acturator模組的/health端點來實現的,而是依靠客戶端心跳的方式來保持服務例項的存活。在Eureka的服務續約與剔除機制下,客戶端的健康狀態從註冊到註冊中心開始都會處於UP狀態,除非心跳終止一段時間之後,服務註冊中心將其剔除。預設的心跳實現方式可以有效檢查客戶端程序是否正常運作,但卻無法保證客戶端應用能夠正常提供服務。

在Spring Cloud Eureka中,可以把Eureka客戶端的健康檢測交給spring-boot-actuator模組的health端點,以實現更加全面的健康狀態維護,設定方式如下:

(1) 在pom.xml中引入spring-boot-starter-actuator模組的依賴

(2) 在application.properties中增加引數配置eureka.client.healthcheck.enabled=true

這裡,Idea裡並沒有提示eureka.client.healthcheck.enabled這個屬性,並且還顯示黃色,讓以為是不是哪裡錯了,根本不敢嘗試。不過百度後,發現有人做了類似實驗,成功了。好吧,可能對這個的學習還不夠,或者就應該給Idea提一個issue。下面給出自定義health check來替換Eureka自帶心跳測試。

自己實現HealthChecker

根據自己health的定義,自己實現一個HealthChecker。這裡簡單模擬。
在eureka-client建立MyHealthChecker,

@Component
public class MyHealthChecker implements HealthIndicator {


    private volatile boolean up = true;

    @Override
    public Health health() {
        if (up) {
            return new Health.Builder().withDetail("aaa_cnt", 10) //自定義監控內容
                    .withDetail("bbb_status", "up").up().build();
        } else {
            return new Health.Builder().withDetail("error", "client is down").down().build();
        }

    }

    public boolean isUp() {
        return up;
    }

    public void setUp(boolean up) {
        this.up = up;
    }
}

然後,暴露出一個介面來控制up狀態。

@RestController
public class HealthSettingController {

    @Autowired
    MyHealthChecker myHealthChecker;

    @RequestMapping("/health/{status}")
    public String up(@PathVariable("status") Boolean status) {
        myHealthChecker.setUp(status);

        return status.toString();
    }

}

 

然後在我們的客戶端eureka-clientp配置檔案裡新增eureka.client.healthcheck.enabled=true

重新啟動,訪問http://localhost:2001/health/false來把我們client端中一個instance設定為down。然後,重新整理eureka server面板,即訪問http://master:1111/, 可以看到,我們的client確實下線了。

其他配置

除了上述配置引數外,下面整理了一些EurekaInstanceConfigBean中定義的配置引數以及對應的說明和預設值,這些引數均以eureka.instance為字首。

通訊協議

預設情況下,Eureka使用Jersey和XStream配合JSON作為Server與Client之間的通訊協議。也可以選擇實現自己的協議來代替。

參考