概述
著名的CAP理論指出相速,一個分布式系統(tǒng)不可能同時滿足C(一致性)料按、A(可用性)和P(分區(qū)容錯性)。由于分區(qū)容錯性在是分布式系統(tǒng)中必須要保證的,因此我們只能在A和C之間進行權衡秽澳。
Eureka Server
提供服務注冊服務,各個節(jié)點啟動后戏羽,會在Eureka Server
中進行注冊担神,這樣Eureka Server
中的服務注冊表中將會存儲所有可用服務節(jié)點的信息,服務節(jié)點的信息可以在界面中直觀的看到始花。
Eureka
看明白了這一點妄讯,因此在設計時就優(yōu)先保證可用性孩锡。在此Zookeeper
保證的是CP, 而Eureka
則是AP。Eureka
各個節(jié)點都是平等的亥贸,幾個節(jié)點掛掉不會影響正常節(jié)點的工作躬窜,剩余的節(jié)點依然可以提供注冊和查詢服務。
在應用啟動后炕置,將會向Eureka Server
發(fā)送心跳,默認周期為30秒荣挨,如果Eureka Server
在多個心跳周期內(nèi)沒有接收到某個節(jié)點的心跳,Eureka Server
將會從服務注冊表中把這個服務節(jié)點移除(默認90秒)朴摊。
其次默垄,Eureka Client
對已經(jīng)獲取到的注冊信息也做了30s緩存。即服務通過eureka
客戶端第一次查詢到可用服務地址后會將結果緩存甚纲,下次再調用時就不會真正向Eureka
發(fā)起HTTP請求了口锭。
再次, 負載均衡組件Ribbon
也有30s緩存介杆。**Ribbon
會從上面提到的Eureka Client
獲取服務列表鹃操,然后將結果緩存30s
。
最后春哨,如果你并不是在Spring Cloud
環(huán)境下使用這些組件(Eureka
, Ribbon
)组民,你的服務啟動后并不會馬上向Eureka
注冊,而是需要等到第一次發(fā)送心跳請求時才會注冊悲靴。心跳請求的發(fā)送間隔也是30s臭胜。(Spring Cloud對此做了修改,服務啟動后會馬上注冊)
Eureka Server
之間通過復制的方式完成數(shù)據(jù)的同步癞尚,Eureka
還提供了客戶端緩存機制耸三,即使所有的Eureka Server
都掛掉,客戶端依然可以利用緩存中的信息消費其他服務的API浇揩。
以上這幾個30秒正是官方wiki上寫服務注冊最長需要2分鐘的原因仪壮。Eureka
通過心跳檢查、客戶端緩存等機制胳徽,確保了系統(tǒng)的高可用性积锅、靈活性和可伸縮性。
而Eureka
的客戶端在向某個Eureka
注冊或時如果發(fā)現(xiàn)連接失敗养盗,則會自動切換至其它節(jié)點缚陷,只要有一臺Eureka
還在,就能保證注冊服務可用(保證可用性)往核,只不過查到的信息可能不是最新的(不保證強一致性)箫爷。
除此之外,Eureka
還有一種自我保護機制,如果在15分鐘內(nèi)超過85%的節(jié)點都沒有正常的心跳虎锚,那么Eureka
就認為客戶端與注冊中心出現(xiàn)了網(wǎng)絡故障硫痰,此時會出現(xiàn)以下幾種情況:
-
Eureka
不再從注冊列表中移除因為長時間沒收到心跳而應該過期的服務 -
Eureka
仍然能夠接受新服務的注冊和查詢請求,但是不會被同步到其它節(jié)點上(即保證當前節(jié)點依然可用) - 當網(wǎng)絡穩(wěn)定時窜护,當前實例新的注冊信息會被同步到其它節(jié)點中
因此效斑, Eureka
可以很好的應對因網(wǎng)絡故障導致部分節(jié)點失去聯(lián)系的情況,而不會像Zookeeper
那樣使整個注冊服務癱瘓柱徙。
創(chuàng)建服務
創(chuàng)建一個Eureka
服務中心十分簡單,只需要引入相關依賴,添加注解,稍作配置即可鳍悠。
1.引入依賴
compile "org.springframework.cloud:spring-cloud-starter-eureka-server:${cloud_eureka}"
2.添加注解
@EnableEurekaServer
3.稍作配置
eureka:
instance:
#配置主機名
hostname: eureka.alpha
appname: reka-alpha
client:
# 、在默認設置下坐搔,Eureka服務注冊中心也會將自己作為客戶端來嘗試注冊它自己藏研,
# 所以我們需要禁用它的客戶端注冊行為。
# 因為當注冊中心將自己作為客戶端注冊的時候概行,發(fā)現(xiàn)在server上的端口被自己占據(jù)了蠢挡,然后就掛了
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://eureka.alpha:9871/eureka
這里訪問的是http://eureka.alpha
,需要修改hosts
文件將eureka.alpha
指向127.0.0.1
,如果不做指向,這里也可以使用localhost
訪問,當然,為了后面的高可用配置建議這里做指向.
4.啟動服務,大功告成
高可用配置
主要是利用多個eureka相互注冊,下例用采用三臺服務器做集群部署,其中每臺eureka
服務器向另外兩臺注冊自己的實例.
這里需要注意如下幾點
1.Eureka Server的同步遵循著一個非常簡單的原則:只要有一條邊將節(jié)點連接,就可以進行信息傳播與同步
2.如果Eureka A的peer指向了B, B的peer指向了C凳忙,那么當服務向A注冊時业踏,B中會有該服務的注冊信息,但是C中沒有涧卵。也就是說勤家,如果你希望只要向一臺Eureka注冊其它所有實例都能得到注冊信息,那么就必須把其它所有節(jié)點都配置到當前Eureka的peer屬性中柳恐。這一邏輯是在PeerAwareInstanceRegistryImpl#replicateToPeers()方法中實現(xiàn)的:
示例代碼中拆分了三個配置,可以根據(jù)這三個配置分別打成jar包運行即可,當然,這里建議配合docker-compose
進行編排部署.
1.在Eureka中伐脖,一個instance
通過一個eureka.instance.instanceId
來唯一標識,如果這個值沒有設置乐设,就采用eureka.instance.metadataMap.instanceId
來代替讼庇。instance
之間通過eureka.instance.appName
來彼此訪問,在spring cloud
中默認值是spring.application.name
,如果沒有設置則為UNKNOWN
近尚。在實際使用中spring.application.name
不可或缺,因為相同名字的應用會被Eureka合并成一個群集蠕啄。eureka.instance.instanceId
也可以不設置,直接使用缺省值(client.hostname:application.name:port
)
,同一個appName
下InstanceId
不能相同戈锻。
2.如果 eureka.client.registerWithEureka
設置成true(默認值true)歼跟,應用啟動時,會利用指定的eureka.client.serviceUrl.defaultZone
注冊到對應的Eureka server中格遭。之后每隔30s(通過eureka.instance.leaseRenewalIntervalInSeconds
來配置)向Eureka server
發(fā)送一次心跳哈街,如果Eureka server
在90s(通過eureka.instance.leaseExpirationDurationInSeconds
配置)內(nèi)沒有收到某個instance
發(fā)來的心跳就會把這個instance
從注冊中心中移走。發(fā)送心跳的操作是一個異步任務如庭,如果發(fā)送失敗叹卷,則以2的指數(shù)形式延長重試的時間,直到達到eureka.instance.leaseRenewalIntervalInSeconds * eureka.client.heartbeatExecutorExponentialBackOffBound
這個上限,之后一直以這個上限值作為重試間隔坪它,直至重新連接到Eureka server
骤竹,并且重新嘗試連接到Eureka server
的次數(shù)是不受限制的。
3.在Eureka server
中每一個instance
都由一個包含大量這個instance
信息的com.netflix.appinfo.InstanceInfo
標識往毡,client
向Eureka server
發(fā)送心跳和更新注冊信息是不相同的蒙揣,InstanceInfo也以固定的頻率發(fā)送到Eureka server
,這些信息在Eureka client
啟動后的40s(通過eureka.client.initialInstanceInfoReplicationIntervalSeconds
配置)首次發(fā)送开瞭,之后每隔30s(通過eureka.client.instanceInfoReplicationIntervalSeconds
配置)發(fā)送一次懒震。
4.如果eureka.client.fetchRegistry
設置成true(默認值true),Eureka client
在啟動時會從Eureka server
獲取注冊信息并緩存到本地嗤详,之后只會增量獲取信息(可以把eureka.client.shouldDisableDelta
設置成false來強制每次都全量獲雀鋈拧)。獲取注冊信息的操作也是一個異步任務葱色,每隔30秒執(zhí)行一次(通過eureka.client.registryFetchIntervalSeconds
配置)递宅,如果操作失敗,也是以2的指數(shù)形式延長重試時間苍狰,直到達到eureka.client.registryFetchIntervalSeconds * eureka.client.cacheRefreshExecutorExponentialBackOffBound
這個上限办龄,之后一直以這個上限值作為重試間隔,直至重新獲取到注冊信息淋昭,并且重新嘗試獲取注冊信息的次數(shù)是不受限制的俐填。
這些任務都是在com.netflix.discovery.DiscoveryClient中啟動,spring cloud
用org.springframework.cloud.netflix.eureka.CloudEurekaClient
對這個類進行了擴展翔忽。
[常見問題]
1.自我保護模式
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.
默認情況下英融,如果Eureka Server
在一定時間內(nèi)沒有接收到某個微服務實例的心跳,Eureka Server
將會注銷該實例(默認90秒)歇式。但是當網(wǎng)絡分區(qū)故障發(fā)生時矢赁,微服務與Eureka Server
之間無法正常通信,以上行為可能變得非常危險了——因為微服務本身其實是健康的贬丛,此時本不應該注銷這個微服務撩银。
Eureka通過“自我保護模式”來解決這個問題——當Eureka Server節(jié)點在短時間內(nèi)丟失過多客戶端時(可能發(fā)生了網(wǎng)絡分區(qū)故障),那么這個節(jié)點就會進入自我保護模式豺憔。一旦進入該模式额获,Eureka Server
就會保護服務注冊表中的信息,不再刪除服務注冊表中的數(shù)據(jù)(也就是不會注銷任何微服務)恭应。當網(wǎng)絡故障恢復后抄邀,該Eureka Server
節(jié)點會自動退出自我保護模式。
綜上昼榛,自我保護模式是一種應對網(wǎng)絡異常的安全保護措施境肾。它的架構哲學是寧可同時保留所有微服務(健康的微服務和不健康的微服務都會保留),也不盲目注銷任何健康的微服務。使用自我保護模式奥喻,可以讓Eureka
集群更加的健壯偶宫、穩(wěn)定。
在Spring Cloud中环鲤,可以使用eureka.server.enable-self-preservation = false
禁用自我保護模式纯趋。
2.unavailable-replicas
在配置文件中如果不使用域名的方式,而指定localhost或者ip(127.0.0.1/外網(wǎng)ip)冷离,服務能夠正常啟動吵冒,但分片服務總顯示在unavailable-replicas
中,因此在host中指定了相應的域名做服務區(qū)分
** 參考 **