Eureka詳解系列(五)--Eureka Server部分的原始碼和配置
阿新 • • 發佈:2021-02-10
# 簡介
按照原定的計劃,我將分三個部分來分析 Eureka 的原始碼:
1. Eureka 的配置體系(已經寫完,見[Eureka詳解系列(三)--探索Eureka強大的配置體系](https://www.cnblogs.com/ZhangZiSheng001/p/14374005.html));
2. Eureka Client 的互動行為(已經寫完,見[Eureka詳解系列(四)--Eureka Client部分的原始碼和配置](https://www.cnblogs.com/ZhangZiSheng001/p/14381169.html) );
3. Eureka Server 的互動行為。
今天,我們來研究第三部分的原始碼。
分析的思路和第二部分的一樣,先明確 Eureka Server 需要具備哪些功能,再從原始碼層面分析如何實現這些功能,最後補充 Eureka Server 的配置解讀。
# 專案環境
os:win 10
jdk:1.8.0_231
eureka:1.10.11
tomcat:9.0.21
# Eureka Server 的功能
還是來回顧 Eureka 的整個互動過程。
首先,Eureka Server 需要和 Eureka Client 互動,所以它需要能夠處理 Eureka Client 的各種請求,這些請求包括:
1. **獲取登錄檔**(Application Client 的請求);
2. **註冊、續約、登出例項**(Application Service 的請求);
除此之外,在叢集中,它需要和對等節點互動,互動內容主要包括:
2. **將自己的登錄檔變更操作同步到其他節點**;
3. **處理其他節點同步登錄檔的請求**。
其實,一個完整的 Eureka Server 專案本身也包含了 Eureka Client 的部分,也就是說,它可以註冊自己和消費包括自己在內的服務,可以在 eureka-client.properties 增加以下配置來關閉掉這兩個部分的功能(不建議這麼做):
```properties
# 當前例項是否註冊到Eureka Server。預設true
eureka.registration.enabled=false
# 當前例項是否需要從Eureka Server獲取服務登錄檔
eureka.shouldFetchRegistry=false
```
# 如何實現這些功能
知道了 Eureka Server 需要具備哪些功能,接下來我們就從原始碼的角度來看看怎樣實現這些功能。
和之前一樣,我更多的會從設計的層面來分析,而不會順序地去看每個過程的程式碼,即重設計、輕實現。
那麼,還是從一個 UML 圖開始吧。有了它,相信大家看原始碼時會更輕鬆一些。
`AbstractInstanceRegistry`裡放了一張登錄檔,用來存放所有的例項物件,通過它可以處理 Eureka Client 或者其他 Eureka Server 的請求,包括註冊、續約、登出例項以及獲取登錄檔等。
它的子類`PeerAwareInstanceRegistryImpl`提供了多節點的支援,這裡以續約例項的方法為例,相同的操作還會被同步到其他節點(對等節點的請求除外)。
```java
public boolean renew(final String appName, final String id, final boolean isReplication) {
// 先呼叫父類AbstractInstanceRegistry的方法
if (super.renew(appName, id, isReplication)) {
// 再將操作同步到其他節點,最終是呼叫PeerEurekaNode的方法進行同步
replicateToPeers(Action.Heartbeat, appName, id, null, null, isReplication);
return true;
}
return false;
}
```
除此之外,`PeerAwareInstanceRegistryImpl`還啟動了三個定時任務:
1. **更新`PeerEurekaNode`列表**。例如,當我們使用 DNS 配合 serviceUrl 時,對等節點的地址可能會變化,所以需要及時更新。這個定時任務用於支援叢集的故障轉移和擴容。
2. **更新引數 numberOfRenewsPerMinThreshold--每分鐘至少要有多少例項續約**。當每分鐘續約例項少於這個值時(eureka 認為是災難性的網路故障導致的),Eureka Server 將進入自我保護模式,此時,它不會再主動淘汰例項,直到我們主動關閉該模式,或者續約例項達到了閾值。我們一般可以通過以下引數來控制。而每分鐘至少要有多少例項續約,這個數值受到例項總數的影響,所以需要定時更新。
```properties
# 期望例項多久續約一次
eureka.expectedClientRenewalIntervalSeconds=30
# 續約例項的閾值,未達到將開啟自我保護模式
eureka.renewalPercentThreshold=0.85
# 是否啟用保護模式
eureka.enableSelfPreservation=true
```
3. **丟棄未能及時續約的例項**。預設情況下,例項超過 90s 未能續約的話,Eureka Server 會將其丟棄掉。
# 從哪裡開始看原始碼
Eureka Server 是作為一個 Web 應用執行的,要看原始碼比較難找到入口。開啟[Eureka詳解系列(二)--如何使用Eureka(原生API,無Spring)](https://www.cnblogs.com/ZhangZiSheng001/p/14337985.html) 例子裡的 web.xml,可以看到配置了一個監聽器,這個類就是 Eureka Server 初始化的入口。
```xml
com.netflix.eureka.EurekaBootStrap
```
在這個類裡面,我們主要關注這一段程式碼(程式碼有刪減)。
```java
protected void initEurekaServerContext() throws Exception {
// 下面這一段是為了初始化Eureka Client所需要的物件,上一篇部落格講過了
EurekaInstanceConfig instanceConfig = new MyDataCenterInstanceConfig();
ApplicationInfoManager applicationInfoManager = new ApplicationInfoManager(
instanceConfig, new EurekaConfigBasedInstanceInfoProvider(instanceConfig).get());
EurekaClientConfig eurekaClientConfig = new DefaultEurekaClientConfig();
eurekaClient = new DiscoveryClient(applicationInfoManager, eurekaClientConfig);
// 載入eureka-server.properties的配置
EurekaServerConfig eurekaServerConfig = new DefaultEurekaServerConfig();
ServerCodecs serverCodecs = new DefaultServerCodecs(eurekaServerConfig);
// 初始化登錄檔物件(支援多節點)
PeerAwareInstanceRegistry registry = new PeerAwareInstanceRegistryImpl(
eurekaServerConfig,
eurekaClient.getEurekaClientConfig(),
serverCodecs,
eurekaClient
);
// 初始化PeerEurekaNodes物件
PeerEurekaNodes peerEurekaNodes = getPeerEurekaNodes(
registry,
eurekaServerConfig,
eurekaClient.getEurekaClientConfig(),
serverCodecs,
applicationInfoManager
);
// 1. 初始化PeerEurekaNode列表,
// 2. 啟動定時任務:更新PeerEurekaNode列表
peerEurekaNodes.start();
// 1. 將PeerEurekaNode列表的指標給到PeerEurekaNodes物件物件
// 2. 啟動定時任務:更新引數numberOfRenewsPerMinThreshold--每分鐘至少要有多少例項續約,它是判斷是否開啟自我保護模式的依據
registry.init(peerEurekaNodes);
// 從其他節點獲取例項列表並註冊到本地的登錄檔
int registryCount = registry.syncUp();
// 1. 初始化引數numberOfRenewsPerMinThreshold--每分鐘要求多少例項續約
// 2. 開啟定時任務:淘汰未能正常續約的例項
registry.openForTraffic(applicationInfoManager, registryCount);
}
```
完成初始化後,Eureka Server 就可以處理 Eureka Client 的請求了。因為 Eureka Server 使用 jersey 作 Web 框架(jersey 和 struts2、springMVC 作用差不多,沒接觸過也不礙事),所以,只要找到添加了`javax.ws.rs.Path`註解的類,就能找到這部分程式碼的入口。
# Eureka Server 的配置解讀
回顧下[Eureka詳解系列(三)--探索Eureka強大的配置體系](https://www.cnblogs.com/ZhangZiSheng001/p/14374005.html)的內容,在 Eureka 裡,配置分成了三種:
1. **EurekaInstanceConfig**:當前例項身份的配置資訊,即**我是誰?**
2. **EurekaServerConfig**:一些影響當前Eureka Server和客戶端或對等節點互動行為的配置資訊,即**怎麼互動?**
3. **EurekaClientConfig**:一些影響當前例項和Eureka Server互動行為的配置資訊,即**和誰互動?怎麼互動?**
這裡我們來講講`EurekaServerConfig`的配置引數,對應的是 eureka-server.properties 裡的配置。
```properties
# 期望例項多久續約一次
eureka.expectedClientRenewalIntervalSeconds=30
# 續約例項的閾值,未達到將開啟自我保護模式
eureka.renewalPercentThreshold=0.85
# 是否啟用保護模式
eureka.enableSelfPreservation=true
# 更新引數numberOfRenewsPerMinThreshold的定時任務多久執行一次
renewalThresholdUpdateIntervalM=900000
# 更新PeerEurekaNode列表的定時任務多久執行一次
peerEurekaNodesUpdateIntervalMs=600000
# 淘汰未能正常續約例項的定時任務多久執行一次
evictionIntervalTimerInMs=60000
# 這幾個一般不用,我就不展開了。有需要的話可以
#awsAccessId=
#awsSecretKey=
eipBindRebindRetries=3
eipBindRebindRetryIntervalMsWhenUnbound=60000
eipBindRebindRetryIntervalMs=300000
waitTimeInMsWhenSyncEmpty=300000
shouldBatchReplication=false
disableDelta=false
numberRegistrySyncRetries=5
registrySyncRetryWaitMs=30000
enableReplicatedRequestCompression=false
minAvailableInstancesForPeerReplication=-1
peerEurekaStatusRefreshTimeIntervalMs=30000
peerNodeConnectTimeoutMs=1000
peerNodeReadTimeoutMs=5000
peerNodeTotalConnections=1000
peerNodeTotalConnectionsPerHost=500
numberOfReplicationRetries=5
maxElementsInPeerReplicationPool=10000
maxIdleThreadAgeInMinutesForPeerReplication=15
minThreadsForPeerReplication=5
maxThreadsForPeerReplication=20
maxTimeForReplication=30000
primeAwsReplicaConnections=true
maxIdleThreadAgeInMinutesForStatusReplication=10
minThreadsForStatusReplication=1
maxThreadsForStatusReplication=1
maxElementsInStatusReplicationPool=10000
disableDeltaForRemoteRegions=false
remoteRegionConnectTimeoutMs=2000
remoteRegionReadTimeoutMs=5000
remoteRegionTotalConnections=1000
remoteRegionTotalConnectionsPerHost=500
remoteRegionConnectionIdleTimeoutSeconds=30
remoteRegion.gzipContent=true
#remoteRegionUrlsWithName=
#remoteRegion.appWhiteList=
remoteRegion.registryFetchIntervalInSeconds=30
remoteRegion.fetchThreadPoolSize=20
#remoteRegion.trustStoreFileName=
remoteRegion.trustStorePassword=changeit
remoteRegion.disable.transparent.fallback=false
shouldUseAwsAsgApi=true
asgQueryTimeoutMs=300
asgUpdateIntervalMs=300000
asgCacheExpiryTimeoutMs=600000
retentionTimeInMSInDeltaQueue=180000
deltaRetentionTimerIntervalInMs=30000
responseCacheAutoExpirationInSeconds=180
responseCacheUpdateIntervalMs=30000
shouldUseReadOnlyResponseCache=true
syncWhenTimestampDiffers=true
auth.shouldLogIdentityHeaders=true
route53BindRebindRetries=3
route53BindRebindRetryIntervalMs=300000
route53DomainTTL=30
initialCapacityOfResponseCache=1000
jsonCodecName=com.netflix.discovery.converters.wrappers.CodecWrappers.LegacyJacksonJson
xmlCodecName=com.netflix.discovery.converters.wrappers.CodecWrappers.XStreamXml
```
以上比較巨集觀地講完了 Eureka Server 的原始碼和配置,具體的細節歡迎私信交流。
最後,感謝您的閱讀。
# 參考資料
https://github.com/Netflix/eureka/wiki/Eureka-at-a-glance
> 相關原始碼請移步:[https://github.com/ZhangZiSheng001/eureka-demo](https://github.com/ZhangZiSheng001/eureka-demo)
>本文為原創文章,轉載請附上原文出處連結:[https://www.cnblogs.com/ZhangZiSheng001/p/14395079.html](https://www.cnblogs.com/ZhangZiSheng001/p/14395079