跟我學Spring Cloud(Finchley版)-06-服務註冊與服務發現-Eureka深入
在跟我學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的配置即可:
-
為系統配置主機名:
vim /etc/hosts # 新增如下內容 127.0.0.1 peer1 peer2 對於Windows系統,請修改C:\windows\system32\drivers\etc\hosts檔案
-
配置:
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叢集。
-
啟動:
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叢集的簡寫方式:
spring:
application:
name: microservice-discovery-eureka-ha
eureka:
client:
serviceUrl:
defaultZone: http://peer2:8762/eureka/,http://peer1:8761/eureka/
---
spring:
profiles: peer1
server:
port: 8761
eureka:
instance:
hostname: peer1
---
spring:
profiles: peer2
server:
port: 8762
eureka:
instance:
hostname: peer2
將應用註冊到Eureka Server叢集上
以microservice-provider-user
專案為例,只須修改eureka.client.serviceUrl.defaultZone
,配置多個Eureka Server地址,就可以將其註冊到Eureka Server叢集了。示例:
eureka:
client:
serviceUrl:
defaultZone: http://peer1:8761/eureka/,http://peer2:8762/eureka/
這樣就可以將服務註冊到Eureka Server叢集上了。
當然,微服務即使只配置Eureka Server叢集中的某個節點,也能正常註冊到Eureka Server叢集,因為多個Eureka Server之間的資料會相互同步。例如:
eureka:
client:
serviceUrl:
defaultZone: http://peer1:8761/eureka/
正常情況下,這種方式與配置多個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 success 404 if instanceID doesn’t exist |
Query for all instances | GET /eureka/apps | HTTP Code: 200 on success Output:JSON/XML |
Query for all appIDinstances | 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 success 500 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 success 500 on failure |
Update metadata | PUT /eureka/apps/appID/instanceID/metadata?key=value | HTTP Code: 200 on success 500 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
<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
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
本文首發
http://www.itmuch.com/spring-cloud/finchley-6/