Eureka Server啟動過程分析
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的一些回調接口實現。