Eureka實(shí)現(xiàn)功能:
- 注冊中心(應(yīng)用程序之間解耦者甲,包括Provider和Consumer之間的解耦,也包括集群模式下的Provider和Provider之間的解耦砌创,不需要知道彼此的地址虏缸,通過eureka地址和serviceId來識(shí)別)
- 靈活的擴(kuò)展性(基于注冊中心的功能,可以靈活的新增或者減少集群中的數(shù)量嫩实,只需要添加和減少serviceId相同的節(jié)點(diǎn)即可)
- 發(fā)現(xiàn)功能刽辙,心跳功能,定時(shí)嘗試連接節(jié)點(diǎn)舶赔,應(yīng)用程序恢復(fù)后重新接入的功能
基本原理
Eureka的高可用性:
- Eureka在設(shè)計(jì)時(shí)就優(yōu)先保證可用性扫倡。Eureka各個(gè)節(jié)點(diǎn)都是平等和獨(dú)立的,任意節(jié)點(diǎn)掛掉不會(huì)影響其它正常節(jié)點(diǎn)的工作竟纳,剩余的正常節(jié)點(diǎn)依然可以提供注冊和查詢服務(wù)撵溃。
- Eureka的客戶端在向某個(gè)Eureka注冊或時(shí)如果發(fā)現(xiàn)連接失敗,則會(huì)自動(dòng)切換至其它節(jié)點(diǎn)锥累,只要有一臺(tái)Eureka還在缘挑,就能保證注冊服務(wù)可用(保證可用性),只不過查到的信息可能不是最新的(不保證強(qiáng)一致性)桶略。因此语淘, Eureka可以很好的應(yīng)對因網(wǎng)絡(luò)故障導(dǎo)致部分節(jié)點(diǎn)失去聯(lián)系的情況,而不會(huì)像zookeeper那樣使整個(gè)注冊服務(wù)癱瘓
EurekaServer集群模式下的工作原理:
- Client只需要向其中的某一個(gè)Server節(jié)點(diǎn)注冊际歼,處于不同節(jié)點(diǎn)的eurekaServer通過Replicate進(jìn)行數(shù)據(jù)同步
- 當(dāng)Client-Consumer要調(diào)用Client-Provider的時(shí)候惶翻,則向服務(wù)注冊中心中的某一個(gè)EurekaServer獲取服務(wù)提供者地址,然后會(huì)將服務(wù)提供者地址緩存在本地鹅心,下次再調(diào)用時(shí)吕粗,則直接從本地緩存中取,完成一次調(diào)用旭愧。
- Eureka Server啟動(dòng)時(shí)會(huì)開啟定時(shí)任務(wù)颅筋,定時(shí)檢測所有注冊的應(yīng)用,當(dāng)檢測到服務(wù)提供者因?yàn)殄礄C(jī)输枯、網(wǎng)絡(luò)原因不可用時(shí)议泵,則在服務(wù)注冊中心將服務(wù)置為DOWN狀態(tài),并把當(dāng)前服務(wù)提供者狀態(tài)向訂閱者發(fā)布桃熄,訂閱過的服務(wù)消費(fèi)者更新本地緩存先口。
- 服務(wù)提供者在啟動(dòng)后,周期性(默認(rèn)30秒)向Eureka Server發(fā)送心跳,以證明當(dāng)前服務(wù)是可用狀態(tài)池充。Eureka Server在一定的時(shí)間(默認(rèn)90秒桩引,實(shí)際是180秒+)未收到客戶端的心跳,則認(rèn)為服務(wù)宕機(jī)收夸,注銷該實(shí)例。
Eureka過期策略原理
https://blog.csdn.net/akaks0/article/details/79512680
概括:
Eureka為了避免因?yàn)榫W(wǎng)絡(luò)抖動(dòng)(Client-Provider和EurekaServer之間的抖動(dòng)血崭,但是Client-Provider和Client-Comsumer之間還可以正常通訊)而剔除了原本還可以工作的微服務(wù)卧惜,所以默認(rèn)不支持剔除故障服務(wù)的功能,需要通過配置開啟過期注銷功能(eureka.server.enable-self-preservation=false)夹纫。EurekaServer在啟動(dòng)時(shí)會(huì)開啟一個(gè)定時(shí)任務(wù)(時(shí)間周期默認(rèn)是60s咽瓷,也支持自定義配置),會(huì)掃描所有心跳響應(yīng)超時(shí)的注冊服務(wù)(需要在注冊服務(wù)里面配置超時(shí)時(shí)間舰讹,Eureka存在bug茅姜,實(shí)際判斷超時(shí)的時(shí)間會(huì)Double配置的時(shí)間,默認(rèn)是90s月匣,Double以后就是180s)钻洒,如果有超時(shí)的應(yīng)用,那么會(huì)根據(jù)注銷因子锄开,逐漸的注銷掉應(yīng)用超時(shí)的微服務(wù)素标。Eureka的自我保護(hù)機(jī)制:不剔除,但是會(huì)報(bào)錯(cuò)
Eureka Server在運(yùn)行期間萍悴,會(huì)統(tǒng)計(jì)心跳失敗的比例在15分鐘之內(nèi)是否低于85%头遭,如果出現(xiàn)低于的情況(在單機(jī)調(diào)試的時(shí)候很容易滿足,實(shí)際在生產(chǎn)環(huán)境上通常是由于網(wǎng)絡(luò)不穩(wěn)定導(dǎo)致)癣诱,Eureka Server會(huì)將當(dāng)前的實(shí)例注冊信息保護(hù)起來计维,同時(shí)提示這個(gè)警告。保護(hù)模式主要用于一組客戶端和Eureka Server之間存在網(wǎng)絡(luò)分區(qū)場景下的保護(hù)撕予。一旦進(jìn)入保護(hù)模式鲫惶,Eureka Server將會(huì)嘗試保護(hù)其服務(wù)注冊表中的信息,不再刪除服務(wù)注冊表中的數(shù)據(jù)(也就是不會(huì)注銷任何微服務(wù))嗅蔬。詳細(xì)過程
核心代碼AbstractInstanceRegistry.class Line 584
- EurekaServer在啟動(dòng)的時(shí)候剑按,會(huì)開啟一個(gè)定時(shí)任務(wù),默認(rèn)是60秒,執(zhí)行一次過期服務(wù)的清理剔除
- 如果enableSelfPreservation是開啟的話(默認(rèn)就是開啟的澜术,為了避免網(wǎng)絡(luò)抖動(dòng)而剔除掉了還可以提供服務(wù)的微服務(wù))艺蝴,則不會(huì)嘗試剔除超時(shí)的節(jié)點(diǎn),所以要手動(dòng)配置為false
- 支持通過配置evictionIntervalTimerInMs=4000參數(shù)來配置,來控制EurekaServer定時(shí)任務(wù)執(zhí)行的時(shí)間間隔
- 每次遍歷所有的注冊應(yīng)用鸟废,判斷其是否超時(shí)過期猜敢,然后統(tǒng)計(jì)出來
- 判斷過期的條件:lease.isExpired(additionalLeaseMs) && lease.getHolder() != null
- isExpired方法,會(huì)先獲取注冊服務(wù)配置的leaseExpirationDurationInSeconds時(shí)間,也就是duration的值缩擂。但是每次在更新lastUpdateTimestamp的時(shí)候鼠冕,都會(huì)把系統(tǒng)時(shí)間自增duration值,這是Eureka的Bug胯盯,注釋里面也做了說明懈费。另外如果注冊的服務(wù)未配置過期時(shí)間萌焰,Lease的默認(rèn)超時(shí)時(shí)間是DEFAULT_DURATION_IN_SECS = 90;累計(jì)上這個(gè)bug卷员,也就是3min后才會(huì)注銷掉這個(gè)服務(wù)淘正。
public static final int DEFAULT_DURATION_IN_SECS = 90;
public boolean isExpired(long additionalLeaseMs) {
return (evictionTimestamp > 0 || System.currentTimeMillis() > (lastUpdateTimestamp + duration + additionalLeaseMs));
}
public void renew() {
lastUpdateTimestamp = System.currentTimeMillis() + duration;}
- 這里面Eureka避免了一次性大量下線蚂子,設(shè)置了一個(gè)最大注銷個(gè)數(shù)的因子盏浙,默認(rèn)0.85昼浦,也就是一次最多下線 registerSize - (int)registerSize*0.85,比如10個(gè)注冊應(yīng)用吞加,那么至少要保留8個(gè)應(yīng)用问麸,允許一次性剔除的最大個(gè)數(shù)是2個(gè)疗杉,如果有問題的節(jié)點(diǎn)時(shí)4個(gè)阵谚,那么這一次最多可以隨機(jī)注銷2個(gè),第二輪遍歷的時(shí)候烟具,8個(gè)節(jié)點(diǎn)庫梢什,最多保留6個(gè),然后正好下線2個(gè)净赴,實(shí)現(xiàn)了一個(gè)平滑的故障服務(wù)注銷
int registrySize = (int) getLocalRegistrySize();
int registrySizeThreshold = (int) (registrySize * serverConfig.getRenewalPercentThreshold());
int evictionLimit = registrySize - registrySizeThreshold;
int toEvict = Math.min(expiredLeases.size(), evictionLimit);
- 這里面的注銷是隨機(jī)注銷的
Random random = new Random(System.currentTimeMillis());
for (int i = 0; i < toEvict; i++) {
int next = i + random.nextInt(expiredLeases.size() - i);
Collections.swap(expiredLeases, i, next);
Lease<InstanceInfo> lease = expiredLeases.get(i);
internalCancel(appName, id, false);
}
- 配置
Eureka Server
eureka:
server:
enableSelfPreservation: false
evictionIntervalTimerInMs: 5000
Application Provider
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8000/eureka/
instance:
leaseRenewalIntervalInSeconds: 10
leaseExpirationDurationInSeconds: 11
實(shí)現(xiàn)功能的邏輯
- Eureka通過配置一個(gè)提供服務(wù)的URL地址绳矩,其它服務(wù)通過這個(gè)url地址來注冊自己
- 每個(gè)注冊的應(yīng)用都有一個(gè)spring.application.name,這個(gè)是最關(guān)心的標(biāo)識(shí)符玖翅,做負(fù)載均衡的話翼馆,applicationName一定要一樣才行。做遠(yuǎn)程調(diào)用的話金度,也是通過這個(gè)applicationName來調(diào)用對應(yīng)程序的服務(wù)
- eureka配置
spring.application.name=spring-cloud-eureka
server.port=8000
// 讓eureka不檢測自己
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/
- 注冊用戶的配置
eureka.client.serviceUrl.defaultZone= http://localhost:8761/eureka/
spring.application.name= spring-cloud-psm-main
eureka 集群的配置
因?yàn)镋ureka作為非常核心的功能应媚,做小的集群是非常必要的。但是要注意的是猜极,其實(shí)客戶端一旦連通之后中姜,會(huì)在自己的內(nèi)存中備份一份遠(yuǎn)程通訊的地址,也就是連通成功后跟伏,Eureka掛了并不影響業(yè)務(wù)丢胚。
- eureka服務(wù)器配置,eureka.client.serviceUrl.defaultZone 相互指向即可受扳。
server:
port: 8011
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8010/eureka/
register-with-eureka: false
fetch-registry: false
instance:
hostname=peer1
server.port=8010
eureka.instance.hostname=peer2
eureka.client.serviceUrl.defaultZone= http://localhost:8011/eureka/
eureka.client.register-with-eureka= false
eureka.client.fetch-registry= false
- eureka客戶端配置携龟,逗號(hào)分隔開即可
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8010/eureka/,http://localhost:8011/eureka/
eureka常用配置匯總
服務(wù)端:設(shè)置清理無效節(jié)點(diǎn)的時(shí)間間隔(單位:毫秒)
eureka.server.eviction-interval-timer-in-ms
客戶端:
eureka.instance.lease-renewal-interval-in-seconds
// 讓eureka不檢測自己
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.client.serviceUrl.defaultZone=http://localhost:${server.port}/eureka/
普通SB項(xiàng)目通過eureka注冊來實(shí)現(xiàn) admin監(jiān)控的配置流程
最關(guān)鍵的:eureka.client.serviceUrl.defaultZone,設(shè)置與Eureka Server交互的地址,查詢服務(wù)和注冊服務(wù)都需要依賴這個(gè)地址勘高∠矿客戶端和服務(wù)端都設(shè)置這個(gè)才能相互發(fā)現(xiàn)坟桅。
- 監(jiān)管admin的配置
eureka:
instance:
leaseRenewalIntervalInSeconds: 10
client:
registryFetchIntervalSeconds: 5
serviceUrl:
defaultZone: ${EUREKA_SERVICE_URL:http://localhost:8761}/eureka/
- 用戶的配置
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
參考出處
http://www.ityouknow.com/springcloud/2017/05/10/springcloud-eureka.html