一、自动装配

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

Spring Cloud系列(四):Eureka源码解析之客户端的更多相关文章

  1. spring cloud深入学习(四)-----eureka源码解析、ribbon解析、声明式调用feign

    基本概念 1.Registe 一一服务注册当eureka Client向Eureka Server注册时,Eureka Client提供自身的元数据,比如IP地址.端口.运行状况指标的Uri.主页地址 ...

  2. Mybatis 系列6-结合源码解析节点配置:objectFactory、databaseIdProvider、plugins、mappers

    [Mybatis 系列10-结合源码解析mybatis 执行流程] [Mybatis 系列9-强大的动态sql 语句] [Mybatis 系列8-结合源码解析select.resultMap的用法] ...

  3. Mybatis 系列3-结合源码解析properties节点和environments节点

    [Mybatis 系列10-结合源码解析mybatis 执行流程] [Mybatis 系列9-强大的动态sql 语句] [Mybatis 系列8-结合源码解析select.resultMap的用法] ...

  4. Mybatis 系列10-结合源码解析mybatis 的执行流程

    [Mybatis 系列10-结合源码解析mybatis 执行流程] [Mybatis 系列9-强大的动态sql 语句] [Mybatis 系列8-结合源码解析select.resultMap的用法] ...

  5. Mybatis 系列8-结合源码解析select、resultMap的用法

    [Mybatis 系列10-结合源码解析mybatis 执行流程] [Mybatis 系列9-强大的动态sql 语句] [Mybatis 系列8-结合源码解析select.resultMap的用法] ...

  6. Mybatis 系列7-结合源码解析核心CRUD 配置及用法

    [Mybatis 系列10-结合源码解析mybatis 执行流程] [Mybatis 系列9-强大的动态sql 语句] [Mybatis 系列8-结合源码解析select.resultMap的用法] ...

  7. Mybatis 系列5-结合源码解析TypeHandler

    [Mybatis 系列10-结合源码解析mybatis 执行流程] [Mybatis 系列9-强大的动态sql 语句] [Mybatis 系列8-结合源码解析select.resultMap的用法] ...

  8. Mybatis 系列4-结合源码解析节点:typeAliases

    [Mybatis 系列10-结合源码解析mybatis 执行流程] [Mybatis 系列9-强大的动态sql 语句] [Mybatis 系列8-结合源码解析select.resultMap的用法] ...

  9. 死磕 java同步系列之CyclicBarrier源码解析——有图有真相

    问题 (1)CyclicBarrier是什么? (2)CyclicBarrier具有什么特性? (3)CyclicBarrier与CountDownLatch的对比? 简介 CyclicBarrier ...

随机推荐

  1. CSAPP bomb分析

    CSAPP bomb分析 问题介绍 这是一个关于反汇编方面的问题,根据已有的二进制代码来推测程序中的特定条件,主要参考了以下各个博客: CSDN 1 CSDN 2 CSDN 3 CSDN 4 stac ...

  2. Python采集CSDN博客排行榜数据

    文章目录 前言 网络爬虫 搜索引擎 爬虫应用 谨防违法 爬虫实战 网页分析 编写代码 运行效果 反爬技术 前言 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知 ...

  3. unity shader

    Unity新的shader叫做 基于物理渲染的shader,先介绍下新的shader具有的特性: Energy Conservation(能量守恒):这是一个基于物理的概念,它确保物体反射的光线不会比 ...

  4. Linux服务器被入侵后的处理过程

    突然,频繁收到一组服务器 ping 监控不可达邮件,赶紧登陆 zabbix 监控系统查看流量状况. 可见流量已经达到了 800M 左右,这肯定不正常了,马上尝试 SSH 登陆系统,不幸的事,由于网络堵 ...

  5. IDEA 2020 集成 Activity插件

    我按照教程打开settings-plugins,搜索actiBPM搜索不到, 于是我就去下载插件,然后再安装,具体步骤如下: 下载插件: 到http://plugins.jetbrains.com/, ...

  6. 通达OA任意用户登录漏洞复现

    前言 今年hw挺火爆的,第一天上来就放王炸,直接搞得hw暂停 昨天晚上无聊,复现了一下通达oa的洞,也有现成的exp可以使用,比较简单 0x00 漏洞概述 通达OA是一套国内常用的办公系统,此次发现的 ...

  7. 在express中使用ES7装饰器构建路由

    在Java的Spring框架中,我们经常会看到类似于@Controller这样的注解,这类代码能够极大的提高我们代码的可读性和复用性.而在Javascript的ES7提案中,有一种新的语法叫做deco ...

  8. Azure Storage 系列(五)通过Azure.Cosmos.Table 类库在.Net 上使用 Table Storage

    一,引言 上一篇文章我们在.NET 项目中添加了 “WindowsAzure.Storage” 的 NuGet 包进行操作Table 数据,但是使用的 “WindowsAzure.Storage”  ...

  9. 抓取 USB 总线LOG

    在实际工作中经常会遇到需要分析USB报文的情形.比如对比不同厂家4G/5G模块.解决实际IP over USB传输效率低下问题. 这时候如果能抓取到 USBMOM 总线的报文将会大有裨益.毕竟所有ho ...

  10. [LeetCode]394. 字符串解码(栈)

    题目 给定一个经过编码的字符串,返回它解码后的字符串. 编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次.注意 k 保证为正整数. ...