Eureka 源码之客户端注册

本文首发公众号:悟空聊架构
上一讲我们讲到 Eureka 的启动过程,这次我们来分析客户端是如何注册的。
https://xie.infoq.cn/article/095419f8ae951a60e48705fbe
本文主要内容如下:
Eureka Client 就是客户端,可以是 Eureka Server 自身,也可以是要注册的服务实例,比如订单服务、商品服务等。
后续讲到 @EnableEurekaClient 注解时,其实是将当前 Application 当作一个 eureka client,注册到 eureka 服务上。
那么 Eureka Client 是如何注册的呢?
我们可以通过 Eureka 源码提供的示例类 ExampleEurekaClient 来看下 Eureka Client 的构造和注册过程。
首先从 main 方法方法看起,只从 main 方法看,看不出来注册的代码在哪,那我们就来研究下底层的源码。
接着我们来一步一步分析 main 里面做了什么事情。
一、初始化配置
1.1 初始化变量
injectEurekaConfiguration() 方法初始化了 Eureka 的一些变量,比如端口号、当前服务的访问路径、是否需要抓取注册表信息等等。
1.2 获取配置文件配置
在这一行代码中,将配置文件 eureka-client.properties 中的配置读取后,放到了 EurekaInstanceConfig 中。这个 EurekaInstanceConfig 是用来初始化 applicationInfoManager 信息管理器的。
看下面代码,创建了一个 MyDataCenterInstanceConfig,其实就是创建了 EurekaInstanceConfig。
那 MyDataCenterInstanceConfig 和 EurekaInstanceConfig 是什么关系呢?
从类图关系中可以看到 MyDataCenterInstanceConfig 继承 PropertiesInstanceConfig 类,实现了 EurekaInstanceConfig 接口。这种接口之前专门讲过,通过接口来获取配置信息,类似这种方法 getXX()。
然后在 PropertiesInstanceConfig 类的构造函数调用了一个工具类,读取了配置文件 eureka-client.properties 中的值。这个隐藏的有点深啊!
1.3 初始实例信息
主要就是构造出 instanceInfo 实例信息。这个里面的信息包含了第一步初始化变量中的配置信息。
1.4 初始化实例信息管理器
就是将 instanceConfig 和 instanceInfo 交给实例信息管理器来管理。
二、构造 EurekaClient
2.1 构造流程
构造 eurekaClient 的代码
DiscoveryClient 是 EurekaClient 的子类,构造 DiscoveryClient 做了以下几件事:
加载配置文件
初始化网络传输组件
将服务实例配置、配置文件配置、网络传输组件都赋值给了 DiscoveryClient。
初始化两个线程,一个用来心跳检测,一个用来刷新缓存。
初始化网络通信组件 EurekaTransport
尝试抓取注册表信息,如果没有抓取到,则从备用的注册表中获取。
初始化调度任务的方法中,启动了定时调度任务:心跳检测 heartbeat、缓存刷新 cacheRefresh
初始化调度任务的方法中,初始化了一个 InstanceInfoReplicator,用来向 eureka server 注册的。
初始化调度任务的方法中,初始化了一个状态变更的监听器 StatusChangeListener,这个里面也有注册的逻辑。
注意:在初始化调度任务的方法,会根据是否设置了抓取注册表信息和是否注册将 eureka-client 注册到 eureka-server 来执行上面的初始化操作。如下代码所示:
三、Eureka Client 注册
3.1 注册流程
Eureka Client 向 Server 注册的代码隐藏的比较深,很难找到,不是直接调用注册的方法,而是通过一个后台线程去做的,而且调用注册方法的类的名字起得也有争议,叫做 InstanceInfoReplicator,“Replicator” 是拷贝副本的意思,而注册其实不是拷贝副本,而是将新的注册信息发送到 eureka server 上去的,所以这个类的名字起得不太好,这也是容易造成找不到注册代码的一个问题。
下面来看下 eureka client 是怎么向 eureka server 注册的。
(1)注册是通过 InstanceInfoReplicator 类来注册的。它是在构造 DiscoveryClient 时创建出来的。
启动了一个延时 40 s 的线程,
(2)然后将一个标志位设置为 true,用来标记是否注册过了。
(3)然后调用注册的方法
register() 里面的核心代码就是
返回的 httpResponse 大家可以想到这是一个 HTTP 请求,eureka client 注册时就是发送的 http 请求。
eurekaTransport:底层的传输组件,在初始化 DiscoveryClient 时初始化出来的。
registrationClient:它是一个抽象类,在初始化 DiscoveryClient 时,通过调用 scheduleServerEndpointTask() 初始化了专门用于注册的 registrationClient,这里就是 SessionedEurekaHttpClient。
instanceInfo:就是要发送给 eureka server 的当前实例信息,用来注册的信息。
(4)发送 post 注册请求
执行 register() 方法,发送注册请求的类是 AbstractJerseyEurekaHttpClient,这个类在工程 eureka-client-jersey2 里面,用到的是 Jersey 框架,国内用这个框架的不多,不用深究。请求的 url 为
注册的方法里面发送了 post 请求。至此,Client 就注册到 Server 那边了。
那么 Server 是如何将注册信息保存到自己注册表里面的呢? 下篇我们再来讲解。
四、总结
Eureka Client 向 Eureka Server 注册的过程:
(1)Eureka Client 初始化了一个 DiscoveryClient,抓取注册表,执行调度任务。
(2)InstanceInfoReplicator 对象启动了一个延迟 40 s 的后台线程,执行注册。
(3)然后使用 AbstractJersey2EurekaHttpClient 发送 post 请求,将 instanceInfo 实例信息发送给 Eureka Server。
时序图如下:
留个问题
我们使用 Eureka 时,Service 启动后,Eureka 很快就发现了 Service 的存在,如下图所示的控制台界面:
并不需要等待 40 s 才能注册到 Eureka,那这又是为什们呢?
下一篇,我们来看下 Eureka Server 是如何将 Eureka Client 发送过来的注册信息保存起来的。
版权声明: 本文为 InfoQ 作者【悟空聊架构】的原创文章。
原文链接:【http://xie.infoq.cn/article/f93b3c1b176e9fba80a594119】。未经作者许可,禁止转载。











评论