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应用详

posted @ 2020-10-06 07:36  toby.xu  阅读(600)  评论(0编辑  收藏  举报