http://www.globalshopping.top
在Spring Cloud(Finchley版)-05-服務註冊與服務發現-Eureka入門 一節中,已經編寫了一個Eureka Server,並將服務提供者與消費者都註冊到了Eureka Server上。
本節,來深入探討Eureka的高級特性。
Eureka原理
本節來探討Eureka的原理。
Region & Availability Zone
下面分析一下Eureka原理,在分析原理前,先來了解一下Region和Availability Zone,如下圖。
衆所周知,Netflix公司將他們的應用都部署在了AWS上,所以Eureka的架構使用到了AWS中的一些概念——不用擔心,這不是說Eureka和AWS環境綁定,Eureka可以部署在任意環境。
Region和Availability Zone均是AWS的概念。
- Region表示AWS中的地理位置,例如us-east-1、us-east-2、eu-west-1等;
- 每個Region都有多個Availability Zone,彼此內網打通;
- 各個Region之間完全隔離,彼此內網不打通;
- AWS通過這種方式實現了最大的容錯和穩定性。
Spring Cloud中,默認使用的Region是us-east-1
。非AWS環境下,可將將Region理解爲內網沒有打通的機房,將Availability Zone理解成相同機房的不同機架(內網打通)。
拓展閱讀
- 如果您不瞭解AWS,那你應該聽說過阿里雲,可以把AWS簡單認爲是美國版的阿里雲……
- 對Region和Availability Zone感興趣的讀者可前往http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html擴展閱讀;
- 2017年AWS的S3發生故障,受影響的大型網站列表中,Netflix赫然在列,有興趣可前往https://www.jianshu.com/p/d5d1fd3151ad 拓展閱讀。
Eureka架構詳解
如圖是Eureka集羣的工作原理。圖中的組件非常多,概念也比較抽象,我們先來用通俗易懂的文字翻譯一下:
-
Application Service:服務提供者;
-
Application Client:服務消費者;
-
Make Remote Call調用RESTful API;
-
us-east-1c、us-east-1d等都是Availability Zone,它們都屬於us-east-1這個region。
由圖可知,Eureka包含兩個組件:Eureka Server 和 Eureka Client,它們的作用如下:
-
Eureka Server提供服務發現的能力,各個微服務啓動時,會向Eureka Server註冊自己的信息(例如IP、端口、微服務名稱等),Eureka Server會存儲這些信息;
-
Eureka Client是一個Java客戶端,用於簡化與Eureka Server的交互;
-
微服務啓動後,會週期性(默認30秒)地向Eureka Server發送心跳以續約自己的“租期”;
-
如果Eureka Server在一定時間內沒有接收到某個微服務實例的心跳,Eureka Server將會註銷該實例(默認90秒);
-
默認情況下,Eureka Server同時也是Eureka Client。多個Eureka Server實例,互相之間通過增量複製的方式,來實現服務註冊表中數據的同步。Eureka Server默認保證在90秒內,Eureka Server集羣內的所有實例中的數據達到一致(從這個架構來看,Eureka Server所有實例所處的角色都是對等的,沒有類似Zookeeper、Consul、Etcd等軟件的選舉過程,也不存在主從,所有的節點都是主節點。Eureka官方將Eureka Server集羣中的所有實例稱爲“對等體(peer)”)
-
Eureka Client會緩存服務註冊表中的信息。這種方式有一定的優勢——首先,微服務無需每次請求都查詢Eureka Server,從而降低了Eureka Server的壓力;其次,即使Eureka Server所有節點都宕掉,服務消費者依然可以使用緩存中的信息找到服務提供者並完成調用。
綜上,Eureka通過心跳檢查、客戶端緩存等機制,提高了系統的靈活性、可伸縮性和可用性。
TIPS
事實上,這個官方架構圖是有一點問題的:Eureka Server本身也集成了Eureka Client,彼此通過Eureka Client同步數據給其它實例又或者從其他實例同步數據——現在,你應該能理解上一節中所使用的 register-with-eureka
以及fetch-registry
的作用了。
高可用
編寫高可用Eureka Server
下面來編寫一個雙節點Eureka Server集羣。編寫這個集羣非常簡單,只需修改單實例Eureka Server的配置即可:
-
爲系統配置主機名:
1 2 3 4 5
vim /etc/hosts # 添加如下內容 127.0.0.1 peer1 peer2 對於Windows系統,請修改C:\windows\system32\drivers\etc\hosts文件
-
配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
spring: application: name: microservice-discovery-eureka-ha --- spring: profiles: peer1 # 指定profile=peer1 server: port: 8761 eureka: instance: hostname: peer1 # 指定當profile=peer1時,主機名是peer1 client: serviceUrl: defaultZone: http://peer2:8762/eureka/ # 將自己註冊到peer2這個Eureka上面去 --- spring: profiles: peer2 server: port: 8762 eureka: instance: hostname: peer2 client: serviceUrl: defaultZone: http://peer1:8761/eureka/
由配置不難看出我們設置了兩個Profile:peer1、peer2。兩個Profile下各有一個Eureka Server,通過相互註冊的方式,構建了Eureka Server集羣。
-
啓動:
1 2
java -jar microservice-discovery-eureka-ha-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer1 java -jar microservice-discovery-eureka-ha-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer2
第一個實例會報錯,這是正常的,因爲它會嘗試連接第二個實例,但第二個實例尚未啓動,所以會報連接不上的異常。
注意點
- 如果兩個Eureka Server實例在同一臺機器上啓動,那麼配置hosts的這一步不能少。原因:Eureka Server對端口是不敏感的,這意味着,如果直接用IP的形式(例如地址寫成
http://127.0.0.1:8761/eureka/
)相互註冊,Eureka Server誤認爲兩個Eureka Server實例是一個實例——這會造成Eureka Server首頁顯示不正常等一系列問題!!
拓展閱讀
- 考慮到有童鞋對Spring Boot的Profile不熟悉,貼個拓展閱讀吧:https://blog.csdn.net/j080624/article/details/80507927
TIPS
編寫Eureka Server集羣的簡寫方式:
|
|
將應用註冊到Eureka Server集羣上
以microservice-provider-user
項目爲例,只須修改eureka.client.serviceUrl.defaultZone
,配置多個Eureka Server地址,就可以將其註冊到Eureka Server集羣了。示例:
|
|
這樣就可以將服務註冊到Eureka Server集羣上了。
當然,微服務即使只配置Eureka Server集羣中的某個節點,也能正常註冊到Eureka Server集羣,因爲多個Eureka Server之間的數據會相互同步。例如:
|
|
正常情況下,這種方式與配置多個Server節點的效果是一樣的。不過爲適應某些極端場景,筆者建議在客戶端配置多個Eureka Server節點。
應用啓動後,訪問Eureka Server應能看到類似如下的界面:
RESTful API
前文說過,Eureka本身是一個基於REST的服務。本節來探討Eureka Server的RESTful API。
下表展示了Eureka Server提供的RESTful API,來自https://github.com/Netflix/eureka/wiki/Eureka-REST-operations ,只需按表格向Eureka Server發送請求,即可操作Eureka Server中的數據。
Operation | HTTP action | Description |
---|---|---|
Register new application instance | POST /eureka/apps/appID | Input:JSON/XMLpayload HTTPCode: 204 on success |
De-register application instance | DELETE /eureka/apps/appID/instanceID | HTTP Code: 200 on success |
Send application instance heartbeat | PUT /eureka/apps/appID/instanceID | HTTP Code: 200 on success404 if instanceID doesn’t exist |
Query for all instances | GET /eureka/apps | HTTP Code: 200 on success Output:JSON/XML |
Query for all appID instances | GET /eureka/apps/appID | HTTP Code: 200 on success Output:JSON/XML |
Query for a specificappID/instanceID | GET /eureka/apps/appID/instanceID | HTTP Code: 200 on success Output:JSON/XML |
Query for a specificinstanceID | GET /eureka/instances/instanceID | HTTP Code: 200 on success Output:JSON/XML |
Take instance out of service | PUT /eureka/apps/appID/instanceID/status?value=OUT_OF_SERVICE | HTTP Code: 200 on success500 on failure |
Put instance back into service (remove override) | DELETE /eureka/apps/appID/instanceID/status?value=UP (The value=UP is optional, it is used as a suggestion for the fallback status due to removal of the override) | HTTP Code: 200 on success500 on failure |
Update metadata | PUT /eureka/apps/appID/instanceID/metadata?key=value | HTTP Code: 200 on success500 on failure |
Query for all instances under a particular vip address | GET /eureka/vips/vipAddress | HTTP Code: 200 on success Output:JSON/XML 404 if thevipAddressdoes not exist. |
Query for all instances under a particular secure vip address | GET /eureka/svips/svipAddress | HTTP Code: 200 on success Output:JSON/XML 404 if thesvipAddressdoes not exist. |
調用示例
示例1:註冊一個服務:
-
將以下文件存儲爲rest-api-test.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
<instance> <instanceId>itmuch:rest-api-test:9000</instanceId> <hostName>itmuch</hostName> <app>REST-API-TEST</app> <ipAddr>127.0.0.1</ipAddr> <vipAddress>rest-api-test</vipAddress> <secureVipAddress>rest-api-test</secureVipAddress> <status>UP</status> <port enabled="true">9000</port> <securePort enabled="false">443</securePort> <homePageUrl>http://127.0.0.1:9000/</homePageUrl> <statusPageUrl>http://127.0.0.1:9000/info</statusPageUrl> <healthCheckUrl>http://127.0.0.1:9000/health</healthCheckUrl> <dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo"> <name>MyOwn</name> </dataCenterInfo> </instance>
-
通過cURL調用Eureka Server
1
cat ./rest-api-test.xml | curl -v -X POST -H "Content-type: application/xml" -d @- http://localhost:8761/eureka/apps/rest-api-test
示例2:查看指定服務的所註冊的信息
只需訪問:http://Eureka Server的地址/eureka/apps/microservice-provider-user
即可查看microdervice-provider-user
服務的信息。
RESTful API的意義
你可能會問:我們不是已經有Eureka Client了嗎?誰閒着沒事再去用RESTful API啊?
要知道,微服務的優勢之一就是允許使用異構的技術、異構的語言甚至異構的平臺解決你想解決的問題。
舉個例子,如果你有一個系統,一部分是Spring Cloud構建的,一部分是用世界上最好的語言PHP寫的!但是呢,你希望Java應用與PHP應用之間的通信也能享受服務發現所帶來的好處,此時就可編寫一個基於PHP的Eureka Client,將PHP應用也註冊到Eureka Server!
事實上,前文說的Eureka Client不過是一個用Jersey 1.x封裝了RESTful API的Jar包而已。
拓展閱讀
事實上,業界已經有一些不同語言的Eureka Client,例如:
- Node.js版的Eureka Client:https://www.npmjs.com/package/eureka-js-client
- Python版的Eureka Client:https://github.com/keijack/python-eureka-client
自我保護模式
自我保護模式是Eureka的重要特性,筆者之前已經專題寫過文章詳解了,所以本系列不再贅述,詳見:理解Eureka的自我保護模式
用戶認證
Finchley版本相對之前的版本有些改動,比較重要。詳見: 跟我學Spring Cloud(Finchley版)番外-01-Eureka安全詳解 。
配套代碼
- GitHub:
- microservice-discovery-eureka-ha:https://github.com/eacdy/spring-cloud-study/tree/master/2018-Finchley/microservice-discovery-eureka-ha
- Gitee:
- microservice-discovery-eureka-ha:https://gitee.com/itmuch/spring-cloud-study/tree/master/2018-Finchley/microservice-discovery-eureka-ha