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

  • 相关阅读:
    微信小程序开发——修改小程序原生checkbox、radio默认样式
    微信小程序开发——微信小程序下拉刷新真机无法弹回
    转:slf4j-api、slf4j-log4j12、log4j之间关系
    MyBatis3 入门学习指南
    Java 多线程重排序的探究
    Kafka 生产者和消费者入门代码基础
    Java面试题
    刻苦读书的故事合集
    Win10 calc.exe 无法打开计算器的解决方法
    Redis(三):set/get 命令源码解析
  • 原文地址:https://www.cnblogs.com/toby-xu/p/13771607.html
Copyright © 2020-2023  润新知