一、自動裝配
1、根據自動裝配原理(詳見:Spring Boot系列(二):Spring Boot自動裝配原理解析),找到spring-cloud-netflix-eureka-client.jar的spring.factories,檢視spring.factories如下:
2、進入EurekaClient的自動裝配類EurekaClientAutoConfiguration:
3、@ConditionalOnBean(EurekaDiscoveryClientConfiguration.Marker.class)也就是說當容器中有EurekaDiscoveryClientCon figuration.Marker.class時,該配置類才起作用。接下來檢視spring.factories,發現還有一個配置類EurekaDiscoveryClientConfigura tion,該配置類就剛剛好往容器中匯入了EurekaDiscoveryClientConfiguration.Marker。
二、EurekaClient
1、EurekaClientAutoConfiguration配置類
1.1 匯入的核心Bean
① EurekaClientConfigBean:初始化eurekaClient配置;
② EurekaInstanceConfigBean:初始化eureka例項配置;
③ EurekaDiscoveryClient:初始化eureka發現客戶端;
④ EurekaServiceRegistry:初始化eureka服務註冊;
⑤ EurekaAutoServiceRegistration:初始化eureka自動服務註冊;
⑥ EurekaClient:初始化eureka客戶端;
1.2 初始化EurekaClient
① 首先看下類的繼承圖:
② CloudEurekaClient構造方法:
點進去會發現它呼叫父類的構造方法super(applicationInfoManager, config, args);最終來到如下方法:
@Inject DiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args, Provider<BackupRegistry> backupRegistryProvider, EndpointRandomizer endpointRandomizer) { //省略...... this.applicationInfoManager = applicationInfoManager; //1、獲取要註冊的服務例項資訊 InstanceInfo myInfo = applicationInfoManager.getInfo(); //省略...... instanceInfo = myInfo; //省略...... //2、定義一些Executor try { // default size of 2 - 1 each for heartbeat and cacheRefresh scheduler = Executors.newScheduledThreadPool(2, new ThreadFactoryBuilder() .setNameFormat("DiscoveryClient-%d") .setDaemon(true) .build()); //心跳Executor heartbeatExecutor = new ThreadPoolExecutor( 1, clientConfig.getHeartbeatExecutorThreadPoolSize(), 0, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new ThreadFactoryBuilder() .setNameFormat("DiscoveryClient-HeartbeatExecutor-%d") .setDaemon(true) .build() ); // use direct handoff //本地快取重新整理Executor cacheRefreshExecutor = new ThreadPoolExecutor( 1, clientConfig.getCacheRefreshExecutorThreadPoolSize(), 0, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new ThreadFactoryBuilder() .setNameFormat("DiscoveryClient-CacheRefreshExecutor-%d") .setDaemon(true) .build() ); // use direct handoff /** * 1、初始化定時拉取服務註冊資訊和服務續約任務 * 2、定義一個狀態變化監聽 * 3、初始化定時服務註冊任務 */ initScheduledTasks(); //省略...... }
③ 進入initScheduledTasks()方法如下:
private void initScheduledTasks() { if (clientConfig.shouldFetchRegistry()) { int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds(); int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound(); //每隔30s執行CacheRefreshThread,重新整理本地快取 scheduler.schedule( new TimedSupervisorTask( "cacheRefresh", scheduler, cacheRefreshExecutor, registryFetchIntervalSeconds, TimeUnit.SECONDS, expBackOffBound, new DiscoveryClient.CacheRefreshThread() ), registryFetchIntervalSeconds, TimeUnit.SECONDS); } if (clientConfig.shouldRegisterWithEureka()) { int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs(); int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound(); //每隔30s發一次心跳 scheduler.schedule( new TimedSupervisorTask( "heartbeat", scheduler, heartbeatExecutor, renewalIntervalInSecs, TimeUnit.SECONDS, expBackOffBound, new DiscoveryClient.HeartbeatThread() ), renewalIntervalInSecs, TimeUnit.SECONDS); //定時服務註冊 instanceInfoReplicator = new InstanceInfoReplicator( this, instanceInfo, clientConfig.getInstanceInfoReplicationIntervalSeconds(), 2); // burstSize //狀態監聽器,當例項狀態改變時會呼叫監聽器的notify方法 statusChangeListener = new ApplicationInfoManager.StatusChangeListener() { //省略...... @Override public void notify(StatusChangeEvent statusChangeEvent) { //省略...... instanceInfoReplicator.onDemandUpdate(); } }; if (clientConfig.shouldOnDemandUpdateStatusChange()) { //往applicationInfoManager裡面註冊監聽器 applicationInfoManager.registerStatusChangeListener(statusChangeListener); } //開啟定時服務註冊任務 instanceInfoReplicator.start(clientConfig.getInitialInstanceInfoReplicationIntervalSeconds()); } else { logger.info("Not registering with Eureka server per configuration"); } }
上面方法核心邏輯:
一:初始化定時拉取服務註冊資訊和服務續約任務;scheduler.schedule(TimedSupervisorTask),執行TimedSupervisorTask.ru n()方法,定時執行2個任務
Ⅰ、CacheRefreshThread:定時重新整理本地註冊列表;
Ⅱ、HeartbeatThread:定時向Eureka服務端傳送心跳,證明自己還活著;
二:定義一個狀態變化監聽來監聽例項狀態的變化;
statusChangeListener = new ApplicationInfoManager.StatusChangeListener(),裡面的核心方法notify(StatusChangeE vent statusChangeEvent),該方法裡面有個instanceInfoReplicator.onDemandUpdate()方法。然後把statusChangeListener監聽器往applicationInfoManager裡面註冊。當例項狀態改變時會呼叫監聽器的notify方法,也就是會呼叫instanceInfoReplicator.onDema ndUpdate()方法。
三:初始化定時服務註冊任務;
呼叫instanceInfoReplicator.start(clientConfig.getInitialInstanceInfoReplicationIntervalSeconds())方法;進入到InstanceInfo Replicator.start(int initialDelayMs)方法如下:
public void start(int initialDelayMs) { if (started.compareAndSet(false, true)) { instanceInfo.setIsDirty(); // for initial register //延遲40s執行 Future next = scheduler.schedule(this, initialDelayMs, TimeUnit.SECONDS); scheduledPeriodicRef.set(next); } }
進入到InstanceInfoReplicator.run()方法如下:
public void run() { try { discoveryClient.refreshInstanceInfo(); Long dirtyTimestamp = instanceInfo.isDirtyWithTime(); if (dirtyTimestamp != null) { //服務註冊,其實就是呼叫EurekaServer服務端服務註冊介面 httpResponse = eurekaTransport.registrationClient.register(instanceInfo); discoveryClient.register(); instanceInfo.unsetIsDirty(dirtyTimestamp); } } catch (Throwable t) { logger.warn("There was a problem with the instance info replicator", t); } finally { //每隔40s執行一次 Future next = scheduler.schedule(this, replicationIntervalSeconds, TimeUnit.SECONDS); scheduledPeriodicRef.set(next); } }
1.3 初始化EurekaAutoServiceRegistration
① EurekaAutoServiceRegistration類的繼承圖:
實現了SmartLifecycle介面,會在EurekaAutoServiceRegistration初始化完成後,根據isAutoStartup為ture執行start方法。
② 進入EurekaAutoServiceRegistration.start()方法:
public void start() { //省略...... if (!this.running.get() && this.registration.getNonSecurePort() > 0) { //服務註冊 this.serviceRegistry.register(this.registration); //釋出InstanceRegisteredEvent事件 this.context.publishEvent(new InstanceRegisteredEvent<>(this, this.registration.getInstanceConfig())); //設定執行為true this.running.set(true); } }
③ 進入EurekaServiceRegistry.register()方法:
該方法裡面reg.getApplicationInfoManager().setInstanceStatus(reg.getInstanceConfig().getInitialStatus());觸發狀態變化監聽器的notify方法,也就是呼叫到了ApplicationInfoManager.StatusChangeListener.notify方法,notify方法裡面執行instanceInfoRep licator.onDemandUpdate()方法,最終呼叫InstanceInfoReplicator.this.run()方法,進行服務註冊。
三、Eureka客戶端流程圖
自此Eureka客戶端原始碼解析完成,Eureka服務端原始碼詳見:Spring Cloud系列(三):Eureka原始碼解析之服務端。Eureka應用詳見:Spring Cloud系列(二):Eureka應用詳解