Eureka源码解析

SpringCloud版本Greenwich.SR2

使用注解@EnableDiscoveryClient,表明这是一个eureka客户端
我们来看一下为什么写了这个注解就可以注册到注册中心。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(EnableDiscoveryClientImportSelector.class)
public @interface EnableDiscoveryClient {

	/**
	 * If true, the ServiceRegistry will automatically register the local server.
	 * @return - {@code true} if you want to automatically register.
	 */
	boolean autoRegister() default true;

}

里面只有一个属性,autoRegister自动注册
引入了这个类EnableDiscoveryClientImportSelector

@Override
public String[] selectImports(AnnotationMetadata metadata) {
/**
* 将类上的需要导入的导入进来
*/
String[] imports = super.selectImports(metadata);
/**
*获取所有的属性,判断autoRegister自动注册是否打开
*/
AnnotationAttributes attributes = AnnotationAttributes.fromMap(
		metadata.getAnnotationAttributes(getAnnotationClass().getName(), true));

boolean autoRegister = attributes.getBoolean("autoRegister");
/**
* 如果打开了自动注册
*/
if (autoRegister) {
	List<String> importsList = new ArrayList<>(Arrays.asList(imports));
	importsList.add(
			"org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationConfiguration");
	imports = importsList.toArray(new String[0]);
}
else {
	Environment env = getEnvironment();
	if (ConfigurableEnvironment.class.isInstance(env)) {
		ConfigurableEnvironment configEnv = (ConfigurableEnvironment) env;
		LinkedHashMap<String, Object> map = new LinkedHashMap<>();
		map.put("spring.cloud.service-registry.auto-registration.enabled", false);
		MapPropertySource propertySource = new MapPropertySource(
				"springCloudDiscoveryClient", map);
		configEnv.getPropertySources().addLast(propertySource);
	}

}

return imports;
}

心得tips:

  1. 不知道怎么看源码就debug比如在上面打断点,下一步就到这了
    在这里插入图片描述不扯远了,我们继续

那么EurekaClient是什么呢?

Eureka结构,关系
LookupService
EurekaClient
netflix 的 DiscoveryClient
EurekaDiscoveryClient
DiscoveryClient
LookupService中定义的方法
/**
*通过appName获取Application
*/
Application getApplication(String appName);
/**
*获取所有的Application
*/
Applications getApplications();
/**
*通过id查找实例
*/
List<InstanceInfo> getInstancesById(String id);
InstanceInfo getNextServerFromEureka(String virtualHostname, boolean secure);
Application的结构

Application就是所有相同的appName对应的实例(InstanceInfo)


    private static Random shuffleRandom = new Random();

    @Override
    public String toString() {
        return "Application [name=" + name + ", isDirty=" + isDirty
                + ", instances=" + instances + ", shuffledInstances="
                + shuffledInstances + ", instancesMap=" + instancesMap + "]";
    }

    private String name;
	/**
	*是否为脏数据
	*/
    @XStreamOmitField
    private volatile boolean isDirty = false;

	/**
	*所有的实例set
	*/
    @XStreamImplicit
    private final Set<InstanceInfo> instances;

    private final AtomicReference<List<InstanceInfo>> shuffledInstances;

	/**
	*instancesMap,key是实例id,InstanceInfo实例对象
	*/
    private final Map<String, InstanceInfo> instancesMap;

InstanceInfo结构对应的微服务的信息

起点EurekaClient
如果没有EurekaClient的话,会创建一个EurekaClient同时创建CloudEurekaClient以及父类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,
		@Autowired(required = false) HealthCheckHandler healthCheckHandler) {
	// If we use the proxy of the ApplicationInfoManager we could run into a
	// problem
	// when shutdown is called on the CloudEurekaClient where the
	// ApplicationInfoManager bean is
	// requested but wont be allowed because we are shutting down. To avoid this
	// we use the
	// object directly.
	ApplicationInfoManager appManager;
	if (AopUtils.isAopProxy(manager)) {
		appManager = ProxyUtils.getTargetObject(manager);
	}
	else {
		appManager = manager;
	}
	//创建一个cloudEurekaClient,会创建一个父类(DiscoveryClient)
	CloudEurekaClient cloudEurekaClient = new CloudEurekaClient(appManager,
			config, this.optionalArgs, this.context);
	cloudEurekaClient.registerHealthCheck(healthCheckHandler);
	return cloudEurekaClient;
}

public CloudEurekaClient(ApplicationInfoManager applicationInfoManager,
			EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs<?> args,
			ApplicationEventPublisher publisher) {
	super(applicationInfoManager, config, args);
	this.applicationInfoManager = applicationInfoManager;
	this.publisher = publisher;
	this.eurekaTransportField = ReflectionUtils.findField(DiscoveryClient.class,
			"eurekaTransport");
	ReflectionUtils.makeAccessible(this.eurekaTransportField);
}
 @Inject
DiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args,
                Provider<BackupRegistry> backupRegistryProvider, EndpointRandomizer endpointRandomizer) {
    //初始化一些需要用到的对象
    if (args != null) {
        this.healthCheckHandlerProvider = args.healthCheckHandlerProvider;
        this.healthCheckCallbackProvider = args.healthCheckCallbackProvider;
        this.eventListeners.addAll(args.getEventListeners());
        this.preRegistrationHandler = args.preRegistrationHandler;
    } else {
        this.healthCheckCallbackProvider = null;
        this.healthCheckHandlerProvider = null;
        this.preRegistrationHandler = null;
    }
    //获取当前的实例
    this.applicationInfoManager = applicationInfoManager;
    InstanceInfo myInfo = applicationInfoManager.getInfo();

    clientConfig = config;
    staticClientConfig = clientConfig;
    transportConfig = config.getTransportConfig();
    instanceInfo = myInfo;
    if (myInfo != null) {
        appPathIdentifier = instanceInfo.getAppName() + "/" + instanceInfo.getId();
    } else {
        logger.warn("Setting instanceInfo to a passed in null value");
    }

    this.backupRegistryProvider = backupRegistryProvider;
    this.endpointRandomizer = endpointRandomizer;
    this.urlRandomizer = new EndpointUtils.InstanceInfoBasedUrlRandomizer(instanceInfo);
    //localRegionApps保存的本地缓存,所有的应用列表
    localRegionApps.set(new Applications());
	//获取服务器的应用的标识,用于对比compareAndSet
    fetchRegistryGeneration = new AtomicLong(0);
	//获取可达的远程区域
    remoteRegionsToFetch = new AtomicReference<String>(clientConfig.fetchRegistryForRemoteRegions());
    remoteRegionsRef = new AtomicReference<>(remoteRegionsToFetch.get() == null ? null : remoteRegionsToFetch.get().split(","));
	//如果允许获取应用列表,初始化应用列表监视器
    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;
    }

    logger.info("Initializing Eureka in region {}", clientConfig.getRegion());
	//如果既不允许注册也不允许获取
    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());

        // This is a bit of hack to allow for existing code using DiscoveryManager.getInstance()
        // to work with DI'd DiscoveryClient
        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;  // no need to setup up an network tasks and we are done
    }

    try {
    	//为心跳和缓存刷新创建线程池,创建定时任务线程池
        // default size of 2 - 1 each for heartbeat and cacheRefresh
        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()
        );  // use direct handoff

        cacheRefreshExecutor = new ThreadPoolExecutor(
                1, clientConfig.getCacheRefreshExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>(),
                new ThreadFactoryBuilder()
                        .setNameFormat("DiscoveryClient-CacheRefreshExecutor-%d")
                        .setDaemon(true)
                        .build()
        );  // use direct handoff

        eurekaTransport = new EurekaTransport();
        //TODO 服务器节点定时任务,以后分析
        scheduleServerEndpointTask(eurekaTransport, args);

        AzToRegionMapper azToRegionMapper;
        if (clientConfig.shouldUseDnsForFetchingServiceUrls()) {
            azToRegionMapper = new DNSBasedAzToRegionMapper(clientConfig);
        } else {
            azToRegionMapper = new PropertyBasedAzToRegionMapper(clientConfig);
        }
        if (null != remoteRegionsToFetch.get()) {
            azToRegionMapper.setRegionsToFetch(remoteRegionsToFetch.get().split(","));
        }
        instanceRegionChecker = new InstanceRegionChecker(azToRegionMapper, clientConfig.getRegion());
    } catch (Throwable e) {
        throw new RuntimeException("Failed to initialize DiscoveryClient!", e);
    }

    if (clientConfig.shouldFetchRegistry() && !fetchRegistry(false)) {
        fetchRegistryFromBackup();
    }

    // call and execute the pre registration handler before all background tasks (inc registration) is started
    if (this.preRegistrationHandler != null) {
        this.preRegistrationHandler.beforeRegistration();
    }

    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);
        }
    }
	//开启定时任务
    // finally, init the schedule tasks (e.g. cluster resolvers, heartbeat, instanceInfo replicator, fetch
    initScheduledTasks();

    try {
    	//监视
        Monitors.registerObject(this);
    } catch (Throwable e) {
        logger.warn("Cannot register timers", e);
    }

    // This is a bit of hack to allow for existing code using DiscoveryManager.getInstance()
    // to work with DI'd DiscoveryClient
    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());
}
开启定时任务
  1. 缓存刷新定时任务
  2. 心跳检测定时任务
  3. 注册定时任务
private void initScheduledTasks() {
	//更新间隔时间
    int renewalIntervalInSecs;
    int expBackOffBound;
    //是否从服务器中获取注册列表
    if (this.clientConfig.shouldFetchRegistry()) {
    	//从客户端配置信息中获取拉取注册信息的间隔时间
        renewalIntervalInSecs = this.clientConfig.getRegistryFetchIntervalSeconds();
        //退避
        expBackOffBound = this.clientConfig.getCacheRefreshExecutorExponentialBackOffBound();
        //定时刷新服务列表,创建一个缓存刷新线程类刷新缓存,详见下方的源码解析
        this.scheduler.schedule(new TimedSupervisorTask("cacheRefresh", this.scheduler, this.cacheRefreshExecutor, renewalIntervalInSecs, TimeUnit.SECONDS, expBackOffBound, new DiscoveryClient.CacheRefreshThread()), (long)renewalIntervalInSecs, TimeUnit.SECONDS);
    }
	//是否允许去服务器端注册自己
    if (this.clientConfig.shouldRegisterWithEureka()) {
        renewalIntervalInSecs = this.instanceInfo.getLeaseInfo().getRenewalIntervalInSecs();
        expBackOffBound = this.clientConfig.getHeartbeatExecutorExponentialBackOffBound();
        logger.info("Starting heartbeat executor: renew interval is: {}", renewalIntervalInSecs);
        //心跳检测
        this.scheduler.schedule(new TimedSupervisorTask("heartbeat", this.scheduler, this.heartbeatExecutor, renewalIntervalInSecs, TimeUnit.SECONDS, expBackOffBound, new DiscoveryClient.HeartbeatThread()), (long)renewalIntervalInSecs, TimeUnit.SECONDS);
        //复制器,定时将自己注册到服务器上
        this.instanceInfoReplicator = new InstanceInfoReplicator(this, this.instanceInfo, this.clientConfig.getInstanceInfoReplicationIntervalSeconds(), 2);
        //状态改变监听
        this.statusChangeListener = new StatusChangeListener() {
            public String getId() {
                return "statusChangeListener";
            }

            public void notify(StatusChangeEvent statusChangeEvent) {
            	//如果状态变为down了
                if (InstanceStatus.DOWN != statusChangeEvent.getStatus() && InstanceStatus.DOWN != statusChangeEvent.getPreviousStatus()) {
                    DiscoveryClient.logger.info("Saw local status change event {}", statusChangeEvent);
                } else {
                    DiscoveryClient.logger.warn("Saw local status change event {}", statusChangeEvent);
                }

                DiscoveryClient.this.instanceInfoReplicator.onDemandUpdate();
            }
        };
        if (this.clientConfig.shouldOnDemandUpdateStatusChange()) {
            this.applicationInfoManager.registerStatusChangeListener(this.statusChangeListener);
        }

        this.instanceInfoReplicator.start(this.clientConfig.getInitialInstanceInfoReplicationIntervalSeconds());
    } else {
        logger.info("Not registering with Eureka server per configuration");
    }

}
缓存刷新的线程类
class CacheRefreshThread implements Runnable {
    CacheRefreshThread() {
    }

    public void run() {
        DiscoveryClient.this.refreshRegistry();
    }
}

void refreshRegistry() {
    try {
        boolean isFetchingRemoteRegionRegistries = this.isFetchingRemoteRegionRegistries();
        boolean remoteRegionsModified = false;
        /**
		 * Comma separated list of regions for which the eureka registry information will be
		 * fetched. It is mandatory to define the availability zones for each of these regions
		 * as returned by availabilityZones. Failing to do so, will result in failure of
		 * discovery client startup.
		 * 这个地方通过debug知道,是获取的fetchRemoteRegionsRegistry属性,上方为属性的注释信息。
		 * 逗号分隔的eureka服务器地址(可以获取注册信息的)
		 * 经过翻译,我依然不知道干嘛的,所以开启debug模式
		 * 结论,eureka可以进行多区域配置,使用fetch-remote-regions-registry可以指定使用哪个区域
		 * 但是指定的区域必须是可达区域
		 * 例如
		 * eureka.client.availability-zones.eureka=eureka
		 * 区域名和区域值
		 * eureka.client.service-url.eureka=http://127.0.0.1:8888/eureka/
		 * 获取上次的远程区域
		 * 如果使用的是defaultZone(大家在项目中常用的配置latestRemoteRegions == null)
		 * 直接跳到下面
		 */
        String latestRemoteRegions = this.clientConfig.fetchRegistryForRemoteRegions();
        if (null != latestRemoteRegions) {
            String currentRemoteRegions = (String)this.remoteRegionsToFetch.get();
            if (!latestRemoteRegions.equals(currentRemoteRegions)) {
                synchronized(this.instanceRegionChecker.getAzToRegionMapper()) {
                    if (this.remoteRegionsToFetch.compareAndSet(currentRemoteRegions, latestRemoteRegions)) {
                        String[] remoteRegions = latestRemoteRegions.split(",");
                        this.remoteRegionsRef.set(remoteRegions);
                        this.instanceRegionChecker.getAzToRegionMapper().setRegionsToFetch(remoteRegions);
                        remoteRegionsModified = true;
                    } else {
                        logger.info("Remote regions to fetch modified concurrently, ignoring change from {} to {}", currentRemoteRegions, latestRemoteRegions);
                    }
                }
            } else {
                this.instanceRegionChecker.getAzToRegionMapper().refreshMapping();
            }
        }
		/**
		* 获取注册的应用
		*/
        boolean success = this.fetchRegistry(remoteRegionsModified);
        if (success) {
        	/**
        	* 获取本地的所有应用的列表
        	*/
            this.registrySize = ((Applications)this.localRegionApps.get()).size();
            /**
            * 上次成功的时间
            */
            this.lastSuccessfulRegistryFetchTimestamp = System.currentTimeMillis();
        }
		/**
		* 记录日志
		*/
        if (logger.isDebugEnabled()) {
            StringBuilder allAppsHashCodes = new StringBuilder();
            allAppsHashCodes.append("Local region apps hashcode: ");
            allAppsHashCodes.append(((Applications)this.localRegionApps.get()).getAppsHashCode());
            allAppsHashCodes.append(", is fetching remote regions? ");
            allAppsHashCodes.append(isFetchingRemoteRegionRegistries);
            Iterator var11 = this.remoteRegionVsApps.entrySet().iterator();

            while(var11.hasNext()) {
                Entry<String, Applications> entry = (Entry)var11.next();
                allAppsHashCodes.append(", Remote region: ");
                allAppsHashCodes.append((String)entry.getKey());
                allAppsHashCodes.append(" , apps hashcode: ");
                allAppsHashCodes.append(((Applications)entry.getValue()).getAppsHashCode());
            }

            logger.debug("Completed cache refresh task for discovery. All Apps hash code is {} ", allAppsHashCodes);
        }
    } catch (Throwable var9) {
        logger.error("Cannot fetch registry from server", var9);
    }

}
更新本地缓存方法,获取注册的应用信息

满足以下条件会去获取全量的注册信息

  1. delta is disabled
  2. 单独的vip地址
  3. 传入的参数要求全量
  4. localRegionApps本地的缓存是空
  5. localRegionApps大小为0
  6. localRegionApps版本为-1
	/**
	* Disable delta property : false
	* Single vip registry refresh property : null
	* Force full registry fetch : false
	* Application is null : false
	* Registered Applications size is zero : true
	* Application version is -1: true
	* 满足以下条件会去获取全量的注册信息
	* 1.delta is disabled
	* 2.单独的vip地址
	* 3.传入的参数要求全量
	* 4.localRegionApps本地的缓存是空
	* 5.localRegionApps大小为0
	* 6.localRegionApps版本为-1
	*/
	private boolean fetchRegistry(boolean forceFullRegistryFetch) {
        Stopwatch tracer = FETCH_REGISTRY_TIMER.start();

        try {
            // If the delta is disabled or if it is the first time, get all
            // applications
            Applications applications = getApplications();

            if (clientConfig.shouldDisableDelta()
                    || (!Strings.isNullOrEmpty(clientConfig.getRegistryRefreshSingleVipAddress()))
                    || forceFullRegistryFetch
                    || (applications == null)
                    || (applications.getRegisteredApplications().size() == 0)
                    || (applications.getVersion() == -1)) //Client application does not have latest library supporting delta
            {
                logger.info("Disable delta property : {}", clientConfig.shouldDisableDelta());
                logger.info("Single vip registry refresh property : {}", clientConfig.getRegistryRefreshSingleVipAddress());
                logger.info("Force full registry fetch : {}", forceFullRegistryFetch);
                logger.info("Application is null : {}", (applications == null));
                logger.info("Registered Applications size is zero : {}",
                        (applications.getRegisteredApplications().size() == 0));
                logger.info("Application version is -1: {}", (applications.getVersion() == -1));
                getAndStoreFullRegistry();
            } else {
                getAndUpdateDelta(applications);
            }
            applications.setAppsHashCode(applications.getReconcileHashCode());
            logTotalInstances();
        } catch (Throwable e) {
            logger.error(PREFIX + "{} - was unable to refresh its cache! status = {}", appPathIdentifier, e.getMessage(), e);
            return false;
        } finally {
            if (tracer != null) {
                tracer.stop();
            }
        }

        /**
        * 更新实例状态前通知缓存刷新
        */
        onCacheRefreshed();

        //基于缓存中刷新的数据,更新远程状态
        updateInstanceRemoteStatus();

        // registry was fetched successfully, so return true
        return true;
    }
全量更新可用实例的本地缓存
  1. 如果有vip地址,那么从vip地址中取,否则从默认中获取
  2. 乐观锁,防止其他线程重复更改
private void getAndStoreFullRegistry() throws Throwable {
	//获取当前更新的版本
    long currentUpdateGeneration = fetchRegistryGeneration.get();

    logger.info("Getting all instance registry info from the eureka server");

    Applications apps = null;
    //如果有vip地址,那么从vip地址中取,否则从默认中获取
    EurekaHttpResponse<Applications> httpResponse = clientConfig.getRegistryRefreshSingleVipAddress() == null
            ? eurekaTransport.queryClient.getApplications(remoteRegionsRef.get())
            : eurekaTransport.queryClient.getVip(clientConfig.getRegistryRefreshSingleVipAddress(), remoteRegionsRef.get());
    if (httpResponse.getStatusCode() == Status.OK.getStatusCode()) {
        apps = httpResponse.getEntity();
    }
    logger.info("The response status is {}", httpResponse.getStatusCode());

    if (apps == null) {
        logger.error("The application is null for some reason. Not storing this information");
        //乐观锁,cas,如果其他线程修改过,不作处理,记录日志
    } else if (fetchRegistryGeneration.compareAndSet(currentUpdateGeneration, currentUpdateGeneration + 1)) {
    	//更新本地缓存
        localRegionApps.set(this.filterAndShuffle(apps));
        logger.debug("Got full registry with apps hashcode {}", apps.getAppsHashCode());
    } else {
        logger.warn("Not updating applications as another thread is updating it already");
    }
}
部分更新
  1. 从eureka服务器中获取指定的region的增量数据
  2. 如果没有数据,尝试全量更新
  3. 如果有数据,通过乐观锁控制版本
  4. 如果hash码不一致全量更新
private void getAndUpdateDelta(Applications applications) throws Throwable {
	//获取更新版本
    long currentUpdateGeneration = fetchRegistryGeneration.get();
	
    Applications delta = null;
    //从eureka服务器中获取指定的region的增量数据
    EurekaHttpResponse<Applications> httpResponse = eurekaTransport.queryClient.getDelta(remoteRegionsRef.get());
    if (httpResponse.getStatusCode() == Status.OK.getStatusCode()) {
        delta = httpResponse.getEntity();
    }
	//如果没有数据,尝试全量更新
    if (delta == null) {
        logger.warn("The server does not allow the delta revision to be applied because it is not safe. "
                + "Hence got the full registry.");
        getAndStoreFullRegistry();
    	//乐观锁,版本控制
    } else if (fetchRegistryGeneration.compareAndSet(currentUpdateGeneration, currentUpdateGeneration + 1)) {
        logger.debug("Got delta update with apps hashcode {}", delta.getAppsHashCode());
        String reconcileHashCode = "";
        //加锁更新数据
        if (fetchRegistryUpdateLock.tryLock()) {
            try {
                updateDelta(delta);
                //本地hash码
                reconcileHashCode = getReconcileHashCode(applications);
            } finally {
                fetchRegistryUpdateLock.unlock();
            }
        } else {
            logger.warn("Cannot acquire update lock, aborting getAndUpdateDelta");
        }
        //比较hash码,如果不一致,全量更新
        if (!reconcileHashCode.equals(delta.getAppsHashCode()) || clientConfig.shouldLogDeltaDiff()) {
            reconcileAndLogDifference(delta, reconcileHashCode);  // this makes a remoteCall
        }
    } else {
        logger.warn("Not updating application delta as another thread is updating it already");
        logger.debug("Ignoring delta update with apps hashcode {}, as another thread is updating it already", delta.getAppsHashCode());
    }
}
心跳检测线程HeartbeatThread

主要是将自己的信息,appName,id发送到服务器

private class HeartbeatThread implements Runnable {

    public void run() {
        if (renew()) {
            lastSuccessfulHeartbeatTimestamp = System.currentTimeMillis();
        }
    }
}

boolean renew() {
    EurekaHttpResponse<InstanceInfo> httpResponse;
    try {
    	//发送心跳包,将自己的信息,appName,id发送到服务器
        httpResponse = eurekaTransport.registrationClient.sendHeartBeat(instanceInfo.getAppName(), instanceInfo.getId(), instanceInfo, null);
        logger.debug(PREFIX + "{} - Heartbeat status: {}", appPathIdentifier, httpResponse.getStatusCode());
        if (httpResponse.getStatusCode() == Status.NOT_FOUND.getStatusCode()) {
            REREGISTER_COUNTER.increment();
            logger.info(PREFIX + "{} - Re-registering apps/{}", appPathIdentifier, instanceInfo.getAppName());
            long timestamp = instanceInfo.setIsDirtyWithTime();
            boolean success = register();
            if (success) {
                instanceInfo.unsetIsDirty(timestamp);
            }
            return success;
        }
        return httpResponse.getStatusCode() == Status.OK.getStatusCode();
    } catch (Throwable e) {
        logger.error(PREFIX + "{} - was unable to send heartbeat!", appPathIdentifier, e);
        return false;
    }
}
InstanceInfoReplicator复制器,定时注册自己
public void start(int initialDelayMs) {
	//如果是停止状态才启动
    if (started.compareAndSet(false, true)) {
    	//将自己设置成帐数据
        instanceInfo.setIsDirty();
        //执行自己的run方法,返回Future
        Future next = scheduler.schedule(this, initialDelayMs, TimeUnit.SECONDS);
        scheduledPeriodicRef.set(next);
    }
}

public void run() {
    try {
    	//刷新服务状态
        discoveryClient.refreshInstanceInfo();
		//脏时间
        Long dirtyTimestamp = instanceInfo.isDirtyWithTime();
        if (dirtyTimestamp != null) {
        	//注册自己
            discoveryClient.register();
            //清除自己脏数据状态
            instanceInfo.unsetIsDirty(dirtyTimestamp);
        }
    } catch (Throwable t) {
        logger.warn("There was a problem with the instance info replicator", t);
    } finally {
        Future next = scheduler.schedule(this, replicationIntervalSeconds, TimeUnit.SECONDS);
        scheduledPeriodicRef.set(next);
    }
}

小结
  1. 创建一个EurekaClient同时创建CloudEurekaClient以及父类DiscoveryClient
Created with Raphaël 2.2.0 开始 创建一个EurekaClient 创建一个CloudEurekaClient 创建一个DiscoveryClient 初始化一些类和对象 如果允许获取应用列表 初始化应用列表监视器 如果允许注册自己 初始化心跳状态监视器 既不注册也不获取 创建两个线程池(心跳和缓存刷新) 如果允许获取应用列表并尝试增量获取注册信息,成功获取 设置application的哈希吗 开启定时任务 缓存刷新定时任务 心跳检测定时任务 注册定时任务 监视器注册DiscoveryClient 结束 yes no yes no yes no yes no

未完

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值