一、自動裝配
1、根據自動裝配原理(詳見:Spring Boot系列(二):Spring Boot自動裝配原理解析),找到spring-cloud-starter-netflix-eureka-server.jar的spring.factories,檢視spring.factories如下:
2、進入EurekaServer的自動裝配類EurekaServerAutoConfiguration:
3、@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)也就是說當容器中有EurekaServerMarkerConfig uration.Marker.class時,該配置類才起作用。接下來看下啟動類EurekaApplication,該啟動類上不光有@SpringBootApplication自動裝配,還有@EnableEurekaServer,開啟EurekaServer。
二、EurekaServer
1、@EnableEurekaServer註解開啟EurekaServer
1.1 點進去@EnableEurekaServer點進去,發現其@Import(EurekaServerMarkerConfiguration.class),匯入了EurekaServer MarkerConfiguration配置類
1.2 EurekaServerMarkerConfiguration配置類,該配置類匯入了EurekaServerMarkerConfiguration.Marker.class。如下:
2、EurekaServerAutoConfiguration配置類
當容器中有EurekaServerMarkerConfiguration.Marker.class,就可以啟用該配置類,接下來詳細看下該配置類為我們做了什麼。
2.1 @Import(EurekaServerInitializerConfiguration.class)
EurekaServerInitializerConfiguration配置類實現了SmartLifecycle,我們知道實現了SmartLifecycle介面的,會在Ioc容器中所有Bean初始化完成後,根據isAutoStartup()方法返回true來執行該配置類的start()
① 進入EurekaServerInitializerConfiguration.start()方法:
public void start() { new Thread(new Runnable() { @Override public void run() { try { //EurekaServerAutoConfiguration->@Bean EurekaServerBootstrap eurekaServerBootstrap.contextInitialized( EurekaServerInitializerConfiguration.this.servletContext); log.info("Started Eureka Server"); //釋出EurekaRegistryAvailableEvent事件 publish(new EurekaRegistryAvailableEvent(getEurekaServerConfig())); //設定執行狀態為true EurekaServerInitializerConfiguration.this.running = true; //釋出EurekaServerStartedEvent事件 publish(new EurekaServerStartedEvent(getEurekaServerConfig())); } catch (Exception ex) { // Help! log.error("Could not initialize Eureka servlet context", ex); } } }).start(); }
② 進入EurekaServerBootstrap.contextInitialized(ServletContext context)方法:
public void contextInitialized(ServletContext context) { try { //初始化EurekaServer的執行環境 initEurekaEnvironment(); //初始化EurekaServer的上下文 initEurekaServerContext(); context.setAttribute(EurekaServerContext.class.getName(), this.serverContext); } catch (Throwable e) { log.error("Cannot bootstrap eureka server :", e); throw new RuntimeException("Cannot bootstrap eureka server :", e); } }
③ 進入EurekaServerBootstrap.initEurekaServerContext()方法:
protected void initEurekaServerContext() throws Exception { //省略非核心程式碼 //從叢集其他節點複製註冊 int registryCount = this.registry.syncUp(); /** * 1、修改狀態為UP * 2、呼叫父類的postInit 開啟一個剔除定時任務,每隔60執行一次,從當前服務清單中把超時(預設90秒)沒有續約剔 */ this.registry.openForTraffic(this.applicationInfoManager, registryCount); // Register all monitoring statistics. EurekaMonitors.registerAllStats(); }
④ PeerAwareInstanceRegistryImpl.syncUp()方法:
該方法中的eurekaClient.getApplications()是通過http呼叫,獲取叢集中的其他節點的所有服務例項。然後遍歷獲取到的apps,根據isRegisterable(instance)判斷是否可註冊,如果可以註冊就呼叫register(instance, instance.getLeaseInfo().getDurationInSecs(), true)進行註冊,註冊實質就是往AbstractInstanceRegistry的屬性private final ConcurrentHashMap<String, Map<String, Lease <InstanceInfo>>> registry = new ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>();中加入服務例項資訊。
⑤ PeerAwareInstanceRegistryImpl.openForTraffic()方法:
該方法核心第一步:applicationInfoManager.setInstanceStatus(InstanceStatus.UP)修改狀態為UP,第二步:呼叫super.postInit();開啟一個剔除定時任務,每隔60執行一次,從當前服務清單中把超時(預設90秒)沒有續約剔除。
⑥ 進入AbstractInstanceRegistry.postInit()方法:
protected void postInit() { renewsLastMin.start(); if (evictionTaskRef.get() != null) { evictionTaskRef.get().cancel(); } //設定剔除任務EvictionTask evictionTaskRef.set(new AbstractInstanceRegistry.EvictionTask()); //每隔60s執行一次EvictionTask的run方法 evictionTimer.schedule(evictionTaskRef.get(), serverConfig.getEvictionIntervalTimerInMs(), serverConfig.getEvictionIntervalTimerInMs()); }
⑦ EvictionTask的run方法:
執行AbstractInstanceRegistry.evict(),剔除邏輯:主要的功能是將登錄檔registry,其實就是一個ConcurrentHashMap的所有註冊例項遍歷下,看哪些是過期的,過期了就加入到expiredLeases中,然後遍歷expiredLeases,執行internalCancel方法把例項狀態修改成DELETED狀態,這樣客戶端就拿不到。
2.2 匯入了一些Bean
① EurekaServerConfig:初始化eurekaServer配置;
② EurekaController:初始化dashboard的相關介面,使用者獲取eurekaServer的相關資訊;
③ PeerAwareInstanceRegistry:初始化叢集登錄檔;
④ PeerEurekaNodes:初始化叢集節點;
⑤ EurekaServerContext:基於eurekaServer配置,登錄檔,叢集節點,以及服務例項初始化eurekaServer上下文;
⑥ EurekaServerBootstrap:初始化eureka啟動類;
⑦ FilterRegistrationBean:往Filter登錄檔裡面註冊一個Jsrsey過濾器;
其中EurekaServerContext的預設實現DefaultEurekaServerContext在初始化的時候會呼叫initialize()方法,流程圖如下:
3、Eureka的Jersey服務
3.1 服務註冊介面
ApplicationResource.addInstance()方法,核心邏輯就是呼叫PeerAwareInstanceRegistryImpl.register(final InstanceInfo info, final boolean isReplication)方法如下:
public void register(final InstanceInfo info, final boolean isReplication) { int leaseDuration = Lease.DEFAULT_DURATION_IN_SECS; if (info.getLeaseInfo() != null && info.getLeaseInfo().getDurationInSecs() > 0) { leaseDuration = info.getLeaseInfo().getDurationInSecs(); } //往登錄檔中註冊例項資訊,然後執行invalidateCache(),把讀寫快取readWriteCacheMap失效掉 super.register(info, leaseDuration, isReplication); //複製到叢集中的其他節 發起http呼叫,呼叫叢集中的其他節點的註冊服務例項介面 replicateToPeers(PeerAwareInstanceRegistryImpl.Action.Register, info.getAppName(), info.getId(), info, null, isReplication); }
3.2 獲取全量例項資訊介面
ApplicationsResource.getContainers()方法如下:
public Response getContainers(@PathParam("version") String version, @HeaderParam(HEADER_ACCEPT) String acceptHeader, @HeaderParam(HEADER_ACCEPT_ENCODING) String acceptEncoding, @HeaderParam(EurekaAccept.HTTP_X_EUREKA_ACCEPT) String eurekaAccept, @Context UriInfo uriInfo, @Nullable @QueryParam("regions") String regionsStr) { //省略...... //1、構建快取key Key cacheKey = new Key(Key.EntityType.Application, ResponseCacheImpl.ALL_APPS, keyType, CurrentRequestVersion.get(), EurekaAccept.fromString(eurekaAccept), regions ); Response response; if (acceptEncoding != null && acceptEncoding.contains(HEADER_GZIP_VALUE)) { //省略...... } else { //2、根據快取key從快取中獲取,首先從只讀快取中取為null,再去讀寫快取中取,然後設定到只讀快取中 response = Response.ok(responseCache.get(cacheKey)) .build(); } return response; }
3.3 獲取增量例項資訊介面
ApplicationsResource.getContainerDifferential()方法,邏輯同獲取全量例項資訊介面一樣,不同在於構建快取key的時候傳入的ALL_APPS_DELTA,而獲取全量例項資訊介面傳入的是ALL_APPS。
3.4 心跳介面
InstanceResource.renewLease()方法,核心邏輯就是呼叫PeerAwareInstanceRegistryImpl.renew(final String appName, final String id, final boolean isReplication)方法進行續約,其實就是設定例項的lastUpdateTimestamp為當前時間+duration
public void renew() { //設定lastUpdateTimestamp為當前時間+duration lastUpdateTimestamp = System.currentTimeMillis() + duration; }
3.5 服務下線介面
InstanceResource.cancelLease()方法,核心就是呼叫PeerAwareInstanceRegistryImpl.cancel(final String appName, final String id,final boolean isReplication)方法進行服務下線,其實就是把例項的狀態設定成DELETE,然後執行invalidateCache(),把讀寫快取readWriteCacheMap失效掉
三、Eureka服務端流程圖
自此Eureka服務端原始碼解析完成,接下來將對Eureka客戶端原始碼進行解析。Eureka應用詳見:Spring Cloud系列(二):Eureka應用詳解