Eureka Server啟動過程分析

寒天峰發表於2021-01-03

1.首先,SpringCloud充分利用了SpringBoot的自動裝配特點
eureka-server的jar包,發現在META-INF下面的配置檔案spring.factories,裡面記錄了Spring Boot啟動的時候會載入EurekaServerAutoConfiguration自動配置類

2.其次,分析EurekaServerAutoConfiguration類的原始碼,我們發現該類的類頭中存在@ConditionalOnBean的註解,如圖

他代表的意思是,如果想要裝配EurekaServerAutoConfigration,Spring容器中必須要有一個Marker的類的例項。
EurekaServerMarkerConfigration.Marker類:

我們通過Find Usages功能查詢,在什麼樣的情況下,我們會向Spring容器中注入這個Marker物件

我們發現,如果在Spring Cloud啟動過程中新增@EnableEurekaServer註解,我們就會向容器中新增這個Marker的例項物件。也就是說,@EnableEurekaServer註解是Spring Cloud自動載入EurekaServerAutoConfiguration的控制開關。

3.再次,我們需要研究載入EurekaServerAutoConfigration類後,會向容器中注入那些Bean,有那些操作

通過structure結構圖,我們發現,EurekaServer類有一些自動逸的Codec實現,PeerEurekaNodes的回撥介面。但是主要的,與EurekaServer啟動相關的Bean有6個:EurekaController、PeerAuareInstanceRegistry、PeerEurekaNodes、EurekaServerContext、EurekaServerBootstrap、FilterRegistationBean。
3.1 EurekaController

注入一個對外的介面——儀表盤/後臺介面,EurekaController。可以在配置檔案中關閉它,但是預設是開啟的狀態。eureka.dashboard.enabled=true

3.2 PeerAuareInstanceRegistry 和 PeerEurekaNodes

PeerAwareInstanceRegistry是一個對等節點感知例項註冊器(叢集模式下注冊服務使用到的註冊器)。並且值得注意的是,EurekaServer叢集模式中各個節點是對等的,沒有主從之分。(這一點和Zookeeper不一樣,zk通過ZAB協議實現了分散式一致性,但是EurekaServer並沒有實現任何分散式演算法)
PeerEurekaNodes是用來輔助封裝對等節點相關的資訊和操作,比如更新叢集中的對等節點。
這裡我們稍微看下RefreshablePeerEurekaNodes的原始碼,我們發現它是在繼承PeerEurekaNodes的基礎上實現了ApplicationListener介面,該介面只定義了一個回撥方法,處理事件的回撥


實際上,例項化RefreshablePeerEurekaNodes物件還是通過父類的構造方法完成的,這個在程式碼的200行中有體現。

**3.3 **EurekaServerContext

向容器中注入了EurekaServerContext的物件,具體為DefaultEurekaServerContext。翻閱DefaultEurekaServerContext原始碼中,我們發現它通過Spring的@PostConstruct註解呼叫了peerEurekaNodes物件的start()方法

具體在看一下64行呼叫的peerEurekaNodes.start()方法做了哪些操作

首先,定義了一個執行緒池Eureka-PeerNodesUpdater——Eureka的更新對等節點執行緒,最終start方法會開啟這個執行緒,不停呼叫93行所指向的updatePeerEurekaNodes()方法。
這個updatePeerEurekaNodes()方法會更新對等節點資訊,因為EurekaServer節點可能會不斷的變化(比如,Eureka叢集中某個節點的上下線)
而updatePeerEurekaNodes()方法主要做了什麼呢?

其實就是Removing no longer available peer nodes和Adding new peer nodes。
至此,總結一下,EurekaServerContext的例項化過程,會啟動更新遠端對等節點的執行緒,不時的重新整理遠端對等節點的資訊。

3.4 EurekaServerBootstrap

注入EurekaServerBootstrap物件,後續的EurekaServer應用的啟動要依靠這個物件

3.5 FilterRegistrationBean

在Jersey框架下新增一個攔截器ServletContainer物件,攔截所有的/eureka/的請求,是的Jersey框架開始管理所有的/eureka/的請求

4.分析完EurekaServerAutoConfiguration具體注入哪些事情後,我們在返回EurekaServerAutoConfiguration的類頭分析

它利用的Spring的@Import註解,在EurekaServerAutoConfigration注入完所有的Bean後,再次呼叫了EurekaServerInitializerConfigration物件,裝配了它。

由於EurekaServerInitializerConfiguration實現了SmartLifecycle介面,所有Spring容器的Bean建立完成後會回撥start()方法。(順便說一下,EurekaServerInitializerConfiguration類沒有顯式的無參方法,所以它的初始化會呼叫預設的無參構造方法,且幾乎不會做什麼操作)

而start()方法會開啟一個後臺執行緒去使用eurekaServerBootstrap物件初始化Context。

4.1 分析eurekaServerBootstrap物件的contextInitialized()方法(在3.4步驟時例項化的)

初始化上下文實際上就是初始化環境變數方法的呼叫和初始化EurekaServerContext()方法。其中initEurekaEnvironment()方法就是讀取配置,如果沒有配置則設定成預設配置

再看看initEurekaServerContext()方法

這個方法中大體分為5步,1.註冊序列化的轉化器;2.對aws的情況進行特殊處理;3.為非IOC容器提供獲取serverContext物件介面;4.從其他註冊中心複製註冊新資訊;5.改變EurekaServer的狀態,EurekaServer啟動完成的標誌
這裡值得關注的是步驟4,如何複製其他節點的註冊資訊到本EurekaServer上。

4.2 registry.syncUp()方法

這裡224行的register()方法是做把遠端獲得過來的註冊資訊註冊到自己的登錄檔中(map中)

register()方法中具體就是在保證執行緒安全的情況下對registry物件進行操作

綜上所述,registry.syncUp()方法就是拷貝其他對等節點的註冊資訊到自己的登錄檔map中

4.3 registry.openForTraffic()方法

這裡,我們肯定選擇Peer的實現,因為Eureka Server的啟動一般都是叢集模式

openForTraffic()方法裡,具體就是設定例項狀態為UP,並呼叫父類的postInit()方法

在com.netflix.eureka.registry.AbstractInstanceRegistry#postInit()方法中,主要是啟動失效剔除定時任務的,具體的任務邏輯在EvicationTask中定義。

這裡呼叫了 AbstractInstanceRegistry的evict()方法

這個方法就是根據失效時常剔除失效的服務
綜上所屬,registry.syncUp()就是開啟一個服務失效剔除執行緒

所以,
Eureka Server啟動過程就是在@EnableEurekaServer開關的基礎上,建立了一些後臺程式去同步註冊資訊,併釋出一個Jersey的web服務。它充分利用了Spring Boot的自動裝配功能和spring的一些回撥介面實現。

相關文章