在 上篇文章中,咱們簡單介紹了EurekaServer自動裝配及啓動流程解析,本篇文章則繼續研究EurekaClient的相關代碼
老規矩,先看spring.factories
文件,其中引入了一個配置類EurekaDiscoveryClientConfigServiceBootstrapConfiguration
java
@ConditionalOnClass(ConfigServicePropertySourceLocator.class) @ConditionalOnProperty(value = "spring.cloud.config.discovery.enabled", matchIfMissing = false) @Configuration @Import({ EurekaDiscoveryClientConfiguration.class, EurekaClientAutoConfiguration.class }) public class EurekaDiscoveryClientConfigServiceBootstrapConfiguration { }
上方兩個註解則是這個配置類是否可以開啓的條件,這裏就再也不展開,直接看它引入的配置類吧git
EurekaDiscoveryClientConfiguration
Marker
類,能夠猜想也是某個地方的開關EurekaClientConfigurationRefresher
這個類看名字就知道這是當配置被動態刷新時的一個處理器,這裏也再也不展開了EurekaHealthCheckHandlerConfiguration
這裏面註冊了一個Eureka健康檢查的處理類,這個健康檢查相關的原理分析能夠參考這篇文章:SpringBoot健康檢查實現原理 EurekaClientAutoConfiguration
這個類裏面全是重點,也是咱們本文的核心github
@Configuration @EnableConfigurationProperties @ConditionalOnClass(EurekaClientConfig.class) @Import(DiscoveryClientOptionalArgsConfiguration.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", "org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration", "org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration"})
首先能夠看到這個類一共包含這些註解,咱們來一一解析比較重要的幾個註解吧spring
@Import(DiscoveryClientOptionalArgsConfiguration.class)
引入了兩個bean,RestTemplateDiscoveryClientOptionalArgs
和MutableDiscoveryClientOptionalArgs
,這兩個類的做用暫且不說bootstrap
@ConditionalOnBean(EurekaDiscoveryClientConfiguration.Marker.class)
剛纔說的Marker
類的做用出來了app
@AutoConfigureBefore
既然必須在這三個類完成自動裝配以後才能進行裝配,那就表明着這三個類確定大有用途,研究一下吧ide
NoopDiscoveryClientAutoConfiguration
故名思意,負責服務發現的類,我們重點關注一下其中的幾個方法oop
init
@PostConstruct public void init() { String host = "localhost"; try { host = InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException e) { log.warn("Cannot get host info: (" + e.getMessage() + ")"); } int port = findPort(); this.serviceInstance = new DefaultServiceInstance( this.environment.getProperty("spring.application.name", "application"), host, port, false); }
這裏構造了一個DefaultServiceInstance
對象,這個對象包含了當前項目的ip+端口+項目名稱fetch
NoopDiscoveryClient
@Bean public DiscoveryClient discoveryClient() { return new NoopDiscoveryClient(this.serviceInstance); }
再深刻看一下這個類ui
public class NoopDiscoveryClient implements DiscoveryClient { public NoopDiscoveryClient(ServiceInstance instance) { } @Override public String description() { return "Spring Cloud No-op DiscoveryClient"; } @Override public List<ServiceInstance> getInstances(String serviceId) { return Collections.emptyList(); } @Override public List<String> getServices() { return Collections.emptyList(); } }
這個類包含了獲取當前實例以及當前服務的方法,可是返回的都是空,那麼是否是會在後面的某個地方被覆蓋呢?
CommonsClientAutoConfiguration
進去深刻了解一下,哎喲,註冊了幾個bean:DiscoveryClientHealthIndicator
、DiscoveryCompositeHealthIndicator
。原來是健康檢查相關的東西,那就忽略了
ServiceRegistryAutoConfiguration
這個配置類中主要註冊了一個bean:ServiceRegistryEndpoint
這個類主要是對外提供對與Eureka狀態的檢查
@ReadOperation public ResponseEntity getStatus() { if (this.registration == null) { return ResponseEntity.status(HttpStatus.NOT_FOUND).body("no registration found"); } return ResponseEntity.ok().body(this.serviceRegistry.getStatus(this.registration)); }
而Eureka的狀態則是經過serviceRegistry
對象獲取的,這個對象會再下方詳細分析
接着來看這個類注入的幾個bean
EurekaClientConfigBean
@Bean @ConditionalOnMissingBean(value = EurekaClientConfig.class, search = SearchStrategy.CURRENT) public EurekaClientConfigBean eurekaClientConfigBean(ConfigurableEnvironment env) { EurekaClientConfigBean client = new EurekaClientConfigBean(); if ("bootstrap".equals(this.env.getProperty("spring.config.name"))) { client.setRegisterWithEureka(false); } return client; }
這個bean中包含了eureka.client.xxx
系列的一些配置,詳細的配置信息能夠參考這裏:https://github.com/shiyujun/s...
EurekaInstanceConfigBean
這個bean中主要是包含eureka實例(eureka.instance.xxx
系列)的一些配置信息,詳細的配置信息同上
RefreshableEurekaClientConfiguration.DiscoveryClient
@Bean(destroyMethod = "shutdown") @ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT) @org.springframework.cloud.context.config.annotation.RefreshScope @Lazy public EurekaClient eurekaClient(ApplicationInfoManager manager, EurekaClientConfig config, EurekaInstanceConfig instance) { manager.getInfo(); // force initialization return new CloudEurekaClient(manager, config, this.optionalArgs, this.context); }
其中CloudEurekaClient
是DiscoveryClient
的子類,而DiscoveryClient
則是EurekaClient的核心類
public CloudEurekaClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs<?> args, ApplicationEventPublisher publisher) { //這裏會調用父類DiscoveryClient的構造方法 super(applicationInfoManager, config, args); this.applicationInfoManager = applicationInfoManager; this.publisher = publisher; this.eurekaTransportField = ReflectionUtils.findField(DiscoveryClient.class, "eurekaTransport"); ReflectionUtils.makeAccessible(this.eurekaTransportField); }
父類的構造方法中執行的代碼塊比較長,一些賦值操做等就忽略了,這裏只摘出比較重要的部分
if (config.shouldFetchRegistry()) { this.registryStalenessMonitor = new ThresholdLevelsMetric(this, METRIC_REGISTRY_PREFIX + "lastUpdateSec_", new long[]{15L, 30L, 60L, 120L, 240L, 480L}); } else { this.registryStalenessMonitor = ThresholdLevelsMetric.NO_OP_METRIC; } if (config.shouldRegisterWithEureka()) { this.heartbeatStalenessMonitor = new ThresholdLevelsMetric(this, METRIC_REGISTRATION_PREFIX + "lastHeartbeatSec_", new long[]{15L, 30L, 60L, 120L, 240L, 480L}); } else { this.heartbeatStalenessMonitor = ThresholdLevelsMetric.NO_OP_METRIC; }
if (!config.shouldRegisterWithEureka() && !config.shouldFetchRegistry()) { logger.info("Client configured to neither register nor query for data."); scheduler = null; heartbeatExecutor = null; cacheRefreshExecutor = null; eurekaTransport = null; instanceRegionChecker = new InstanceRegionChecker(new PropertyBasedAzToRegionMapper(config), clientConfig.getRegion()); DiscoveryManager.getInstance().setDiscoveryClient(this); DiscoveryManager.getInstance().setEurekaClientConfig(config); initTimestampMs = System.currentTimeMillis(); logger.info("Discovery Client initialized at timestamp {} with initial instances count: {}", initTimestampMs, this.getApplications().size()); return; }
try { scheduler = Executors.newScheduledThreadPool(2, new ThreadFactoryBuilder() .setNameFormat("DiscoveryClient-%d") .setDaemon(true) .build()); heartbeatExecutor = new ThreadPoolExecutor( 1, clientConfig.getHeartbeatExecutorThreadPoolSize(), 0, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new ThreadFactoryBuilder() .setNameFormat("DiscoveryClient-HeartbeatExecutor-%d") .setDaemon(true) .build() ); cacheRefreshExecutor = new ThreadPoolExecutor( 1, clientConfig.getCacheRefreshExecutorThreadPoolSize(), 0, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new ThreadFactoryBuilder() .setNameFormat("DiscoveryClient-CacheRefreshExecutor-%d") .setDaemon(true) .build() );
if (clientConfig.shouldFetchRegistry() && !fetchRegistry(false)) { fetchRegistryFromBackup(); }
這裏fetchRegistry
是第一次拉取註冊信息,若是拉取不成功的話則執行fetchRegistryFromBackup
從備份註冊中心獲取,一樣,拉取的信息會放在以後的文章中
if (this.preRegistrationHandler != null) { this.preRegistrationHandler.beforeRegistration(); }
這裏是個空的實現,能夠經過實現PreRegistrationHandler
接口作些什麼操做
if (clientConfig.shouldRegisterWithEureka() && clientConfig.shouldEnforceRegistrationAtInit()) { try { if (!register() ) { throw new IllegalStateException("Registration error at startup. Invalid server response."); } } catch (Throwable th) { logger.error("Registration error at startup: {}", th.getMessage()); throw new IllegalStateException(th); } }
註冊方法爲register
,一樣這裏先不展開
initScheduledTasks(); private void initScheduledTasks() { // 從 EurekaServer 拉取註冊信息 if (clientConfig.shouldFetchRegistry()) { int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds(); int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound(); scheduler.schedule( new TimedSupervisorTask( "cacheRefresh", scheduler, cacheRefreshExecutor, registryFetchIntervalSeconds, TimeUnit.SECONDS, expBackOffBound, new CacheRefreshThread() ), registryFetchIntervalSeconds, TimeUnit.SECONDS); } // 向 EurekaServer 發送續租心跳 if (clientConfig.shouldRegisterWithEureka()) { int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs(); int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound(); logger.info("Starting heartbeat executor: " + "renew interval is: " + renewalIntervalInSecs); scheduler.schedule( new TimedSupervisorTask( "heartbeat", scheduler, heartbeatExecutor, renewalIntervalInSecs, TimeUnit.SECONDS, expBackOffBound, new HeartbeatThread() ), renewalIntervalInSecs, TimeUnit.SECONDS); instanceInfoReplicator = new InstanceInfoReplicator( this, instanceInfo, clientConfig.getInstanceInfoReplicationIntervalSeconds(), 2); statusChangeListener = new ApplicationInfoManager.StatusChangeListener() { @Override public String getId() { return "statusChangeListener"; } @Override public void notify(StatusChangeEvent statusChangeEvent) { if (InstanceStatus.DOWN == statusChangeEvent.getStatus() || InstanceStatus.DOWN == statusChangeEvent.getPreviousStatus()) { logger.warn("Saw local status change event {}", statusChangeEvent); } else { logger.info("Saw local status change event {}", statusChangeEvent); } instanceInfoReplicator.onDemandUpdate(); } }; if (clientConfig.shouldOnDemandUpdateStatusChange()) { //註冊狀態監聽器 applicationInfoManager.registerStatusChangeListener(statusChangeListener); } instanceInfoReplicator.start(clientConfig.getInitialInstanceInfoReplicationIntervalSeconds()); } else { logger.info("Not registering with Eureka server per configuration"); } }
至此,EurekaClient的自動裝配與啓動流程就解析完畢了