spring cloud服務發現和註冊源碼分析

概述

Spring Cloud中默認的服務發現採用的netflix的eureka,本篇文章就是閱讀Spring cloud中通過eureka做服務發現時的筆記。

順藤摸瓜

讀取Spring各種框架的時候,很多時候不知道從什麼地方開始,因為Spring中很多模塊的開啟就是通過一行註解,例如@EnableXXX。而Spring Cloud中的配置服務則是通過@EnableDiscoveryClient,(其實@EnableEurekaClient就是@EnableDiscoveryClient)

/*** Annotation to enable a DiscoveryClient implementation.* @author Spencer Gibb*/@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@Import(EnableDiscoveryClientImportSelector.class)public @interface EnableDiscoveryClient {/*** If true, the ServiceRegistry will automatically register the local server.*/boolean autoRegister() default true;}

上面有一個@Import(EnableDiscoveryClientImportSelector.class),然後看一下EnableDiscoveryClientImportSelector這個類

public class EnableDiscoveryClientImportSelectorextends SpringFactoryImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata metadata) {...boolean autoRegister = attributes.getBoolean("autoRegister");if (autoRegister) {List importsList = new ArrayList<>(Arrays.asList(imports));importsList.add("org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration");imports = importsList.toArray(new String[0]);}return imports;}}

一看代碼第一反應就是引入了org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration這個類,再看一下這個類

@Configuration@EnableConfigurationProperties(AutoServiceRegistrationProperties.class)public class AutoServiceRegistrationConfiguration {}

什麼都沒有,怎麼回事。其實機關就是在SpringFactoryImportSelector這個類。其實他會把類全命名作為自動配置類的key,瞭解Spring Boot自動加載過程的就應該知道在某個jar包下META-INFO/spring.factories。

org.springframework.cloud.client.discovery.EnableDiscoveryClient=\org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration

這樣,Spring Cloud啟動的時候初始化Eureka就是在EurekaDiscoveryClientConfiguration這個類中。仔細一看,也不對啊,這個類只註冊了一個Marker類,而且這個類是個空類。再全局搜索一下哪裡引用了這個Marker類,終於發現真正初始化Eureka的類:EurekaClientAutoConfiguration

@Configuration@EnableConfigurationProperties@ConditionalOnClass(EurekaClientConfig.class)@ConditionalOnBean(EurekaDiscoveryClientConfiguration.Marker.class)@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)@AutoConfigureBefore({ NoopDiscoveryClientAutoConfiguration.class,CommonsClientAutoConfiguration.class, ServiceRegistryAutoConfiguration.class })@AutoConfigureAfter(name = "org.springframework.cloud.autoconfigure.RefreshAutoConfiguration")public class EurekaClientAutoConfiguration

追根溯源

既然找到了EurekaClientAutoConfiguration這個配置類,那麼肯定有初始化和NetFlix相關類,仔細一看,就是

@Bean(destroyMethod = "shutdown")@ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT)@org.springframework.cloud.context.config.annotation.RefreshScope@Lazypublic EurekaClient eurekaClient(ApplicationInfoManager manager, EurekaClientConfig config, EurekaInstanceConfig instance) {manager.getInfo(); // force initializationreturn new CloudEurekaClient(manager, config, this.optionalArgs,this.context);}

spring cloud服務發現和註冊源碼分析

終於發現和NetFlix的鏈接的地方,CloudEurekaClient繼承的DiscoveryClient其實就是NetFlix的服務發現類,這樣,就可以好好分析是怎麼初始化的。

直擊要害

CloudEurekaClient在初始化的時候主要是調用父類DiscoveryClient的構造函數,會做很多事,其中註冊服務和發現服務是通過調度任務來完成,調度任務的初始化是在initScheduledTasks這個而方法中,其中服務發現的代碼

ate void initScheduledTasks() { if (clientConfig.shouldFetchRegistry()) { // registry cache refresh timer int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds(); int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound(); scheduler.schedule( new TimedSupervisorTask( "cacheRefresh", scheduler, cacheRefreshExecutor, registryFetchIntervalSeconds, TimeUnit.SECONDS, expBackOffBound, new CacheRefreshThread() ), registryFetchIntervalSeconds, TimeUnit.SECONDS); }

首先判斷是否需要進行服務發現,然後通過一個定時任務去刷新緩存信息。TimedSupervisorTask是支持timeout的調度任務,刷新緩存邏輯實際是在CacheRefreshThread()中,往後看就會看到通過Http請求和Erureka服務器交互。服務註冊的代碼緊接著在服務發現後面

if (clientConfig.shouldRegisterWithEureka()) { ... // Heartbeat timer scheduler.schedule( new TimedSupervisorTask( "heartbeat", scheduler, heartbeatExecutor, renewalIntervalInSecs, TimeUnit.SECONDS, expBackOffBound, new HeartbeatThread() ), renewalIntervalInSecs, TimeUnit.SECONDS); // InstanceInfo replicator instanceInfoReplicator = new InstanceInfoReplicator( this, instanceInfo, clientConfig.getInstanceInfoReplicationIntervalSeconds(), 2); // burstSize ... if (clientConfig.shouldOnDemandUpdateStatusChange()) { applicationInfoManager.registerStatusChangeListener(statusChangeListener); } instanceInfoReplicator.start(clientConfig.getInitialInstanceInfoReplicationIntervalSeconds()); }

instanceInfoReplicator對當前服務信息( instanceInfo)進行註冊。

public void run() { ... discoveryClient.refreshInstanceInfo(); Long dirtyTimestamp = instanceInfo.isDirtyWithTime(); if (dirtyTimestamp != null) { discoveryClient.register(); instanceInfo.unsetIsDirty(dirtyTimestamp); } ...}

另外一個心跳檢測的定時任務則是對服務進行續約

boolean renew() { EurekaHttpResponse httpResponse; try { httpResponse = eurekaTransport.registrationClient.sendHeartBeat(instanceInfo.getAppName(), instanceInfo.getId(), instanceInfo, null); if (httpResponse.getStatusCode() == 404) { ... return register(); } return httpResponse.getStatusCode() == 200; } catch (Throwable e) { logger.error("{} - was unable to send heartbeat!", PREFIX + appPathIdentifier, e); return false; } }

可以看出如果續約失敗則重新發起一次註冊。那麼服務註冊的具體實現就在register()中

boolean register() throws Throwable { EurekaHttpResponse httpResponse; try { httpResponse = eurekaTransport.registrationClient.register(instanceInfo); } catch (Exception e) { logger.warn("{} - registration failed {}", PREFIX + appPathIdentifier, e.getMessage(), e); throw e; } if (logger.isInfoEnabled()) { logger.info("{} - registration status: {}", PREFIX + appPathIdentifier, httpResponse.getStatusCode()); } return httpResponse.getStatusCode() == 204;}

實現十分簡單,就是發起一次http調用,把當前instanceInfo傳遞過去。,那麼InstanceInfo都有什麼呢,其實就是一些appName ,url,port等等,通過這些信息我們就能發起一次基於http的rpc調用。

小結

可以看出,Spring cloud中的服務發現和註冊其實就是和通過http方式和eureka進行通信,實現手段則是通過定時任務進行定時操作,包括定時查詢,服務續約。

tencent://AddContact/?fromId=50&fromSubId=1&subcmd=all&uin=2284732365


分享到:


相關文章: