领导让我研究 Eureka 源码 | 注册过程 #yyds干货盘点#

著论准过秦,作赋拟子虚。这篇文章主要讲述领导让我研究 Eureka 源码 | 注册过程 #yyds干货盘点#相关的知识,希望能为你提供帮助。

领导让我研究 Eureka 源码 | 注册过程 #yyds干货盘点#

文章图片

这是悟空的第 124 篇原创文章。欢迎关注我的公众号:悟空聊架构
原文首发
上一讲我们讲到 Eureka 的启动过程:
领导让我研究 Eureka 源码 | 启动过程
这次我们来分析客户端是如何注册的。
本文主要内容如下:
领导让我研究 Eureka 源码 | 注册过程 #yyds干货盘点#

文章图片

Eureka Client 就是客户端,可以是 Eureka Server 自身,也可以是要注册的服务实例,比如订单服务、商品服务等。
后续讲到 @EnableEurekaClient 注解时,其实是将当前 Application 当作一个 eureka client,注册到 eureka 服务上。
那么 Eureka Client 是如何注册的呢?
我们可以通过 Eureka 源码提供的示例类 ExampleEurekaClient 来看下 Eureka Client 的构造和注册过程。
首先从 main 方法方法看起,但是只看 main 表面,看不出来注册的代码在哪,那我们就来研究下底层的源码。
public static void main(String[] args) throws UnknownHostException {injectEurekaConfiguration(); ExampleEurekaClient sampleClient = new ExampleEurekaClient(); // create the client ApplicationInfoManager applicationInfoManager = initializeApplicationInfoManager(new MyDataCenterInstanceConfig()); EurekaClient client = initializeEurekaClient(applicationInfoManager, new DefaultEurekaClientConfig()); // shutdown the client eurekaClient.shutdown(); }

接着我们来一步一步分析 main 里面做了什么事情。
先放一张时序图,下文会逐步讲解。
领导让我研究 Eureka 源码 | 注册过程 #yyds干货盘点#

文章图片

一、初始化配置 1.1 初始化变量
injectEurekaConfiguration() 方法初始化了 Eureka 的一些变量,比如端口号、当前服务的访问路径、是否需要抓取注册表信息等等。
private static void injectEurekaConfiguration() throws UnknownHostException { String myHostName = InetAddress.getLocalHost().getHostName(); String myServiceUrl = "http://" + myHostName + ":8080/v2/"; System.setProperty("eureka.name", "eureka"); System.setProperty("eureka.port", "8080"); ... }

1.2 获取配置文件配置
在这一行代码中,将配置文件 eureka-client.properties 中的配置读取后,放到了 EurekaInstanceConfig 中。这个 EurekaInstanceConfig 是用来初始化 applicationInfoManager 信息管理器的。
看下面代码,创建了一个 MyDataCenterInstanceConfig,其实就是创建了 EurekaInstanceConfig。
new MyDataCenterInstanceConfig()

那 MyDataCenterInstanceConfig 和EurekaInstanceConfig 是什么关系呢?
领导让我研究 Eureka 源码 | 注册过程 #yyds干货盘点#

文章图片

InstanceConfig 类图
从类图关系中可以看到 MyDataCenterInstanceConfig 继承 PropertiesInstanceConfig 类,实现了 EurekaInstanceConfig 接口。这种接口之前专门讲过,通过接口来获取配置信息,类似这种方法 getXX()。
然后在 PropertiesInstanceConfig类的构造函数调用了一个工具类,读取了配置文件 eureka-client.properties 中的值。这个隐藏的有点深啊!
Archaius1Utils.initConfig(CommonConstants.CONFIG_FILE_NAME);

1.3 初始化实例信息
主要就是构造出 instanceInfo 实例信息。这个里面的信息包含了第一步初始化变量中的配置信息。
InstanceInfo instanceInfo = new EurekaConfigBasedInstanceInfoProvider(instanceConfig).get();

intanceInfo 信息如下所示:
领导让我研究 Eureka 源码 | 注册过程 #yyds干货盘点#

文章图片

1.4 初始化实例信息管理器
就是将 instanceConfig 和 instanceInfo 交给实例信息管理器来管理。
applicationInfoManager = new ApplicationInfoManager(instanceConfig, instanceInfo);

二、构造 EurekaClient 2.1 构造流程
构造 eurekaClient 的代码
eurekaClient = new DiscoveryClient(applicationInfoManager, clientConfig);

DiscoveryClient 是 EurekaClient 的子类,构造 DiscoveryClient做了以下几件事:
领导让我研究 Eureka 源码 | 注册过程 #yyds干货盘点#

文章图片

构造 EurekaClient 流程
  • 加载配置文件
  • 初始化网络传输组件
  • 将服务实例配置、配置文件配置、网络传输组件都赋值给了 DiscoveryClient。
  • 初始化两个线程,一个用来心跳检测,一个用来刷新缓存。
  • 初始化网络通信组件 EurekaTransport
  • 尝试抓取注册表信息,如果没有抓取到,则从备用的注册表中获取。
  • 初始化调度任务的方法中,启动了定时调度任务:心跳检测 heartbeat、缓存刷新 cacheRefresh。(这两个功能在后续篇章中都会讲到,请持续关注)
  • 初始化调度任务的方法中,初始化了一个 InstanceInfoReplicator,用来向 eureka server 注册的。
  • 初始化调度任务的方法中,初始化了一个状态变更的监听器 StatusChangeListener,这个里面也有注册的逻辑。
如下代码所示:
领导让我研究 Eureka 源码 | 注册过程 #yyds干货盘点#

文章图片

三、Eureka Client 注册 3.1 注册流程
Eureka Client 向 Server 注册的代码隐藏的比较深,很难找到,不是直接调用注册的方法,而是通过一个后台线程去做的,而且调用注册方法的类的名字起得也有争议,叫做 InstanceInfoReplicator,“Replicator” 是拷贝副本的意思,而注册其实不是拷贝副本,而是将新的注册信息发送到 eureka server 上去的,所以这个类的名字起得不太好,这也是容易造成找不到注册代码的一个原因。
下面来看下 eureka client 是怎么向 eureka server 注册的。
(1)注册是通过 InstanceInfoReplicator 类来注册的。它是在构造 DiscoveryClient 时创建出来的。
启动了一个延时 40 s 的线程,
instanceInfoReplicator.start(40); // 40 s后执行

(2)然后将一个标志位设置为 true,用来标记是否注册过了。
instanceInfo.setIsDirty();

【领导让我研究 Eureka 源码 | 注册过程 #yyds干货盘点#】(3)然后调用注册的方法
discoveryClient.register();

register() 里面的核心代码就是
httpResponse = eurekaTransport.registrationClient.register(instanceInfo);

返回的 httpResponse 大家可以想到这是一个 HTTP 请求,eureka client 注册时就是发送的 http 请求。
eurekaTransport:底层的传输组件,在初始化 DiscoveryClient 时初始化出来的。
registrationClient:它是一个抽象类,在初始化 DiscoveryClient时,通过调用 scheduleServerEndpointTask() 初始化了专门用于注册的 registrationClient,这里就是 SessionedEurekaHttpClient。
instanceInfo:就是要发送给 eureka server 的当前实例信息,用来注册的信息。
(4)发送 post 注册请求
执行 register() 方法,发送注册请求的类是 AbstractJerseyEurekaHttpClient,这个类在工程 eureka-client-jersey2 里面,用到的是 Jersey 框架,国内用这个框架的不多,就是一个支持 restful 的 java 框架,不用深究,下篇还会讲到这一块。请求的 url 为
领导让我研究 Eureka 源码 | 注册过程 #yyds干货盘点#

文章图片

注册的方法里面发送了 post 请求。至此,Client 就注册到 Server 那边了。
response = resourceBuilder .accept(MediaType.APPLICATION_JSON) .acceptEncoding("gzip") .post(Entity.json(info));

那么 Server 是如何将注册信息保存到自己注册表里面的呢? 下篇我们再来讲解。
四、总结Eureka Client 向 Eureka Server 注册的过程:
(1)Eureka Client 初始化了一个 DiscoveryClient,抓取注册表,执行调度任务。
(2)InstanceInfoReplicator 对象启动了一个延迟 40 s 的后台线程,执行注册。
(3)然后使用 AbstractJersey2EurekaHttpClient 发送 post 请求,将 instanceInfo 实例信息发送给 Eureka Server。
时序图如下:
领导让我研究 Eureka 源码 | 注册过程 #yyds干货盘点#

文章图片

留个问题
我们使用 Eureka 时,Service 启动后,Eureka 很快就发现了 Service 的存在,如下图所示的控制台界面:
领导让我研究 Eureka 源码 | 注册过程 #yyds干货盘点#

文章图片

并不需要等待 40 s 才能注册到 Eureka,那这又是为什们呢?
好了,本篇接近尾声,下一篇,我们来看下 Eureka Server 是如何将 Eureka Client 发送过来的注册信息保存起来的。
我是悟空,努力变强,变身超级赛亚人!

    推荐阅读