Spring Cloud系列(四):Eureka源码解析之客户端
一、自动装配
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应用详解