日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 >

eureka同步原理_eureka原理剖析

發布時間:2025/3/20 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 eureka同步原理_eureka原理剖析 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Eureka是Netflix開源的一款提供服務注冊和發現的產品,github地址為 https://github.com/Netflix/eureka。注冊中心是分布式開發的核心組件之一,而eureka是spring cloud推薦的注冊中心實現,因此對于Java開發同學來說,還是有必要學習eureka的,特別是其架構及設計思想。

官方文檔定義是:Eureka is a REST (Representational State Transfer) based service that is primarily used in the AWS cloud for locating services for the purpose of load balancing and failover of middle-tier servers. We call this service, the Eureka Server. Eureka also comes with a Java-based client component,the Eureka Client, which makes interactions with the service much easier. The client also has a built-in load balancer that does basic round-robin load balancing.

Eureka是一個REST (Representational State Transfer)服務,它主要用于AWS云,用于定位服務,以實現中間層服務器的負載平衡和故障轉移,我們稱此服務為Eureka服務器。Eureka也有一個基于java的客戶端組件,Eureka客戶端,這使得與服務的交互更加容易,同時客戶端也有一個內置的負載平衡器,它執行基本的循環負載均衡。

Eureka提供了完整的Service Registry和Service Discovery實現,并且也經受住了Netflix自己的生產環境考驗,相對使用起來會比較省心(同時Spring Cloud還有一套非常完善的開源代碼來整合Eureka,所以使用起來非常方便)。

本文主要內容有:eureka基礎概念及架構、服務發現原理、eureka server/client流程分析及優缺點分析,最后做個小結。由于本文側重于原理分析,因此eureka(結合spring cloud)的使用就不再贅述了,感興趣的小伙伴可以看下 程序猿DD 關于spring cloud的相關教程。

eureka基礎

eureka架構圖

  • Eureka Server:提供服務注冊和發現,多個Eureka Server之間會同步數據,做到狀態一致(最終一致性)
  • Service Provider:服務提供方,將自身服務注冊到Eureka,從而使服務消費方能夠找到
  • Service Consumer:服務消費方,從Eureka獲取注冊服務列表,從而能夠消費服務

注意,上圖中的3個角色都是邏輯角色,在實際運行中,這幾個角色甚至可以是同一個項目(JVM進程)中。

自我保護機制

自我保護機制主要在Eureka Client和Eureka Server之間存在網絡分區的情況下發揮保護作用,在服務器端和客戶端都有對應實現。假設在某種特定的情況下(如網絡故障), Eureka Client和Eureka Server無法進行通信,此時Eureka Client無法向Eureka Server發起注冊和續約請求,Eureka Server中就可能因注冊表中的服務實例租約出現大量過期而面臨被剔除的危險,然而此時的Eureka Client可能是處于健康狀態的(可接受服務訪問),如果直接將注冊表中大量過期的服務實例租約剔除顯然是不合理的,自我保護機制提高了eureka的服務可用性。

當自我保護機制觸發時,Eureka不再從注冊列表中移除因為長時間沒收到心跳而應該過期的服務,仍能查詢服務信息并且接受新服務注冊請求,也就是其他功能是正常的。這里思考下,如果eureka節點A觸發自我保護機制過程中,有新服務注冊了然后網絡回復后,其他peer節點能收到A節點的新服務信息,數據同步到peer過程中是有網絡異常重試的,也就是說,是能保證最終一致性的。

服務發現原理

eureka server可以集群部署,多個節點之間會進行(異步方式)數據同步,保證數據最終一致性,Eureka Server作為一個開箱即用的服務注冊中心,提供的功能包括:服務注冊、接收服務心跳、服務剔除、服務下線等。需要注意的是,Eureka Server同時也是一個Eureka Client,在不禁止Eureka Server的客戶端行為時,它會向它配置文件中的其他Eureka Server進行拉取注冊表、服務注冊和發送心跳等操作。

eureka server端通過appName和instanceInfoId來唯一區分一個服務實例,服務實例信息是保存在哪里呢?其實就是一個Map中:

// 第一層的key是appName,第二層的key是instanceInfoIdprivate final ConcurrentHashMap>> registry = new ConcurrentHashMap>>();

服務注冊

Service Provider啟動時會將服務信息(InstanceInfo)發送給eureka server,eureka server接收到之后會寫入registry中,服務注冊默認過期時間DEFAULT_DURATION_IN_SECS = 90秒。InstanceInfo寫入到本地registry之后,然后同步給其他peer節點,對應方法com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#replicateToPeers。

寫入本地registry

服務信息(InstanceInfo)保存在Lease中,寫入本地registry對應方法com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#register,Lease統一保存在內存的ConcurrentHashMap中,在服務注冊過程中,首先加個讀鎖,然后從registry中判斷該Lease是否已存在,如果已存在則比較lastDirtyTimestamp時間戳,取二者最大的服務信息,避免發生數據覆蓋。使用InstanceInfo創建一個新的InstanceInfo:

if (existingLastDirtyTimestamp > registrationLastDirtyTimestamp) { // 已存在Lease則比較時間戳,取二者最大值 registrant = existingLease.getHolder();}Lease lease = new Lease(registrant, leaseDuration);if (existingLease != null) { // 已存在Lease則取上次up時間戳 lease.setServiceUpTimestamp(existingLease.getServiceUpTimestamp());}?public Lease(T r, int durationInSecs) { holder = r; registrationTimestamp = System.currentTimeMillis(); // 當前時間 lastUpdateTimestamp = registrationTimestamp; duration = (durationInSecs * 1000);}

不知道小伙伴看了上述方法的代碼有沒有這樣的疑問?

通過讀鎖并且 registry 的讀取和寫入不是原子的,那么在并發時其實是有可能發生數據覆蓋的,如果發生數據覆蓋豈不是有問題了!猛一看會以為臟數據不就是有問題么?換個角度想,臟數據就一定有問題么?

其實針對這個問題,eureka的處理方式是沒有問題的,該方法并發時,針對InstanceInfo Lease的構造,二者的信息是基本一致的,因為registrationTimestamp取的就是當前時間,所以并并發的數據不會產生問題。

同步給其他peer

InstanceInfo寫入到本地registry之后,然后同步給其他peer節點,對應方法com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#replicateToPeers。如果當前節點接收到的InstanceInfo本身就是另一個節點同步來的,則不會繼續同步給其他節點,避免形成“廣播效應”;InstanceInfo同步時會排除當前節點。

InstanceInfo的狀態有依以下幾種:Heartbeat, Register, Cancel, StatusUpdate, DeleteStatusOverride,默認情況下同步操作時批量異步執行的,同步請求首先緩存到Map中,key為requestType+appName+id,然后由發送線程將請求發送到peer節點。

Peer之間的狀態是采用異步的方式同步的,所以不保證節點間的狀態一定是一致的,不過基本能保證最終狀態是一致的。結合服務發現的場景,實際上也并不需要節點間的狀態強一致。在一段時間內(比如30秒),節點A比節點B多一個服務實例或少一個服務實例,在業務上也是完全可以接受的(Service Consumer側一般也會實現錯誤重試和負載均衡機制)。所以按照CAP理論,Eureka的選擇就是放棄C,選擇AP。

如果同步過程中,出現了異常怎么辦呢,這時會根據異常信息做對應的處理,如果是讀取超時或者網絡連接異常,則稍后重試;如果其他異常則打印錯誤日志不再后續處理。

服務續約

Renew(服務續約)操作由Service Provider定期調用,類似于heartbeat。主要是用來告訴Eureka Server Service Provider還活著,避免服務被剔除掉。renew接口實現方式和register基本一致:首先更新自身狀態,再同步到其它Peer,服務續約也就是把過期時間設置為當前時間加上duration的值。

注意:服務注冊如果InstanceInfo不存在則加入,存在則更新;而服務預約只是進行更新,如果InstanceInfo不存在直接返回false。

服務下線

Cancel(服務下線)一般在Service Provider shutdown的時候調用,用來把自身的服務從Eureka Server中刪除,以防客戶端調用不存在的服務,eureka從本地”刪除“(設置為刪除狀態)之后會同步給其他peer,對應方法com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#cancel。

服務失效剔除

Eureka Server中有一個EvictionTask,用于檢查服務是否失效。Eviction(失效服務剔除)用來定期(默認為每60秒)在Eureka Server檢測失效的服務,檢測標準就是超過一定時間沒有Renew的服務。默認失效時間為90秒,也就是如果有服務超過90秒沒有向Eureka Server發起Renew請求的話,就會被當做失效服務剔除掉。失效時間可以通過eureka.instance.leaseExpirationDurationInSeconds進行配置,定期掃描時間可以通過eureka.server.evictionIntervalTimerInMs進行配置。

服務剔除#evict方法中有很多限制,都是為了保證Eureka Server的可用性:比如自我保護時期不能進行服務剔除操作、過期操作是分批進行、服務剔除是隨機逐個剔除,剔除均勻分布在所有應用中,防止在同一時間內同一服務集群中的服務全部過期被剔除,以致大量剔除發生時,在未進行自我保護前促使了程序的崩潰。

eureka server/client流程

服務信息拉取

Eureka consumer服務信息的拉取分為全量式拉取和增量式拉取,eureka consumer啟動時進行全量拉取,運行過程中由定時任務進行增量式拉取,如果網絡出現異常,可能導致先拉取的數據被舊數據覆蓋(比如上一次拉取線程獲取結果較慢,數據已更新情況下使用返回結果再次更新,導致數據版本落后),產生臟數據。對此,eureka通過類型AtomicLong的fetchRegistryGeneration對數據版本進行跟蹤,版本不一致則表示此次拉取到的數據已過期。

fetchRegistryGeneration過程是在拉取數據之前,執行fetchRegistryGeneration.get獲取當前版本號,獲取到數據之后,通過fetchRegistryGeneration.compareAndSet來判斷當前版本號是否已更新。

注意:如果增量式更新出現意外,會再次進行一次全量拉取更新。

Eureka server的伸縮容

Eureka Server是怎么知道有多少Peer的呢?Eureka Server在啟動后會調用EurekaClientConfig.getEurekaServerServiceUrls來獲取所有的Peer節點,并且會定期更新。定期更新頻率可以通過eureka.server.peerEurekaNodesUpdateIntervalMs配置。

這個方法的默認實現是從配置文件讀取,所以如果Eureka Server節點相對固定的話,可以通過在配置文件中配置來實現。如果希望能更靈活的控制Eureka Server節點,比如動態擴容/縮容,那么可以override getEurekaServerServiceUrls方法,提供自己的實現,比如我們的項目中會通過數據庫讀取Eureka Server列表。

eureka server啟動時把自己當做是Service Consumer從其它Peer Eureka獲取所有服務的注冊信息。然后對每個服務信息,在自己這里執行Register,isReplication=true,從而完成初始化。

Service Provider

Service Provider啟動時首先時注冊到Eureka Service上,這樣其他消費者才能進行服務調用,除了在啟動時之外,只要實例狀態信息有變化,也會注冊到Eureka Service。需要注意的是,需要確保配置eureka.client.registerWithEureka=true。register邏輯在方法AbstractJerseyEurekaHttpClient.register中,Service Provider會依次注冊到配置的Eureka Server Url上,如果注冊出現異常,則會繼續注冊其他的url。

Renew操作會在Service Provider端定期發起,用來通知Eureka Server自己還活著。 這里instance.leaseRenewalIntervalInSeconds屬性表示Renew頻率。默認是30秒,也就是每30秒會向Eureka Server發起Renew操作。這部分邏輯在HeartbeatThread類中。在Service Provider服務shutdown的時候,需要及時通知Eureka Server把自己剔除,從而避免客戶端調用已經下線的服務,邏輯本身比較簡單,通過對方法標記@PreDestroy,從而在服務shutdown的時候會被觸發。

Service Consumer

Service Consumer這塊的實現相對就簡單一些,因為它只涉及到從Eureka Server獲取服務列表和更新服務列表。Service Consumer在啟動時會從Eureka Server獲取所有服務列表,并在本地緩存。需要注意的是,需要確保配置eureka.client.shouldFetchRegistry=true。由于在本地有一份Service Registries緩存,所以需要定期更新,定期更新頻率可以通過eureka.client.registryFetchIntervalSeconds配置。

小結

為什么要用eureka呢,因為分布式開發架構中,任何單點的服務都不能保證不會中斷,因此需要服務發現機制,某個節點中斷后,服務消費者能及時感知到保證服務高可用。從eureka的設計與實現上來說還是容易理解的,SpringCloud將它集成在自己的子項目spring-cloud-netflix中,實現SpringCloud的服務發現功能。

注冊中心除了用eureka之外,還有zookeeper、consul、nacos等解決方案,他們實現原理不同,各自適用于不同的場景,可按需使用。

Eureka比ZooKeeper相比優勢是什么

Zookeeper保證CP 當向注冊中心查詢服務列表時,我們可以容忍注冊中心返回的是幾分鐘以前的注冊信息,但不能接受服務直接down掉不可用。也就是說,服務注冊功能對可用性的要求要高于一致性。但是zk會出現這樣一種情況,當master節點因為網絡故障與其他節點失去聯系時,剩余節點會重新進行leader選舉。問題在于,選舉leader的時間太長,30 ~ 120s, 且選舉期間整個zk集群都是不可用的,這就導致在選舉期間注冊服務癱瘓。在云部署的環境下,因網絡問題使得zk集群失去master節點是較大概率會發生的事,雖然服務能夠最終恢復,但是漫長的選舉時間導致的注冊長期不可用是不能容忍的。

Eureka保證AP Eureka看明白了這一點,因此在設計時就優先保證可用性。Eureka各個節點都是平等的,幾個節點掛掉不會影響正常節點的工作,剩余的節點依然可以提供注冊和查詢服務。而Eureka的客戶端在向某個Eureka注冊或時如果發現連接失敗,則會自動切換至其它節點,只要有一臺Eureka還在,就能保證注冊服務可用(保證可用性),只不過查到的信息可能不是最新的(不保證強一致性)。除此之外,Eureka還有一種自我保護機制,如果在15分鐘內超過85%的節點都沒有正常的心跳,那么Eureka就認為客戶端與注冊中心出現了網絡故障。

eureka有哪些不足: eureka consumer本身有緩存,服務狀態更新滯后,最常見的狀況就是,服務下線了但是服務消費者還未及時感知,此時調用到已下線服務會導致請求失敗,只能依靠consumer端的容錯機制來保證。

總結

以上是生活随笔為你收集整理的eureka同步原理_eureka原理剖析的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。