从头学习SpringCloud

从头学习SpringCloud(四)Eureka服务注册

2020-07-22  本文已影响0人  Batistuta9

这篇文章来看一下DiscoveryClient类是怎么实现服务注册的。

DiscoveryClient类有一个initScheduledTasks方法。下面是initScheduledTasks的源码。

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

        if (clientConfig.shouldRegisterWithEureka()) {
            int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs();
            int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound();
            logger.info("Starting heartbeat executor: " + "renew interval is: {}", renewalIntervalInSecs);

            // Heartbeat timer
            heartbeatTask = new TimedSupervisorTask(
                    "heartbeat",
                    scheduler,
                    heartbeatExecutor,
                    renewalIntervalInSecs,
                    TimeUnit.SECONDS,
                    expBackOffBound,
                    new HeartbeatThread()
            );
            scheduler.schedule(
                    heartbeatTask,
                    renewalIntervalInSecs, TimeUnit.SECONDS);

            // InstanceInfo replicator
            instanceInfoReplicator = new InstanceInfoReplicator(
                    this,
                    instanceInfo,
                    clientConfig.getInstanceInfoReplicationIntervalSeconds(),
                    2); // burstSize

            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()) {
                        // log at warn level if DOWN was involved
                        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");
        }
    }

注释说明这个方法初始化了所有的规定任务。服务注册也是在这个方法里。
有一段if分支是clientConfig.shouldRegisterWithEureka(),判断是否发起注册,其实就是获取配置文件中的registration.enabled,系统默认为true。
在这个分支内会创建一个InstanceInfoReplicator类,注释说明是“复制实例信息”。该类的完整代码如下。

/**
 * A task for updating and replicating the local instanceinfo to the remote server. Properties of this task are:
 * - configured with a single update thread to guarantee sequential update to the remote server
 * - update tasks can be scheduled on-demand via onDemandUpdate()
 * - task processing is rate limited by burstSize
 * - a new update task is always scheduled automatically after an earlier update task. However if an on-demand task
 *   is started, the scheduled automatic update task is discarded (and a new one will be scheduled after the new
 *   on-demand update).
 *
 *   @author dliu
 */

翻译一下这个类的注释:这个任务用来更新和复制本地的实例信息到远程服务器。这个类的特征是:配置一个单独的更新线程来保证顺序更新到远程服务器;任务更新可以通过"onDemandUpdate()"按需安排;任务处理速率受burstSize限制;在较早的更新任务之后,新的更新任务始终会被自动计划。 但是,如果启动了某项按需任务,则计划的自动更新任务将被废弃(并且,按需更新之后将安排新的计划任务)。
InstanceInfoReplicator的start方法来开启应用实例信息复制器。

public void start(int initialDelayMs) {
        if (started.compareAndSet(false, true)) {
            instanceInfo.setIsDirty();  // for initial register
            Future next = scheduler.schedule(this, initialDelayMs, TimeUnit.SECONDS);
            scheduledPeriodicRef.set(next);
        }
    }

执行 instanceInfo.setIsDirty() 代码块,因为 InstanceInfo 刚被创建时,在 Eureka-Server 不存在,也会被注册。

InstanceInfoReplicator会执行一个定时任务,定时检查 InstanceInfo 的状态( status ) 属性是否发生变化。若是,发起注册。具体工作看一下run()方法的实现。run()方法中有一句discoveryClient.register();这个就是注册的方法了。代码如下。

/**
     * Register with the eureka service by making the appropriate REST call.
     */
    boolean register() throws Throwable {
        logger.info(PREFIX + "{}: registering service...", appPathIdentifier);
        EurekaHttpResponse<Void> httpResponse;
        try {
            httpResponse = eurekaTransport.registrationClient.register(instanceInfo);
        } catch (Exception e) {
            logger.warn(PREFIX + "{} - registration failed {}", appPathIdentifier, e.getMessage(), e);
            throw e;
        }
        if (logger.isInfoEnabled()) {
            logger.info(PREFIX + "{} - registration status: {}", appPathIdentifier, httpResponse.getStatusCode());
        }
        return httpResponse.getStatusCode() == Status.NO_CONTENT.getStatusCode();
    }

调用 DiscoveryClient#refreshInstanceInfo() 方法,刷新应用实例信息。
调用 DiscoveryClient#register() 方法,Eureka-Client 向 Eureka-Server 注册应用实例。
调用 ScheduledExecutorService#schedule(...) 方法,再次延迟执行任务,并设置 scheduledPeriodicRef。通过这样的方式,不断循环定时执行任务。

上一篇下一篇

猜你喜欢

热点阅读