写点什么

Eureka 源码之客户端注册

发布于: 2021 年 11 月 04 日
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 方法看,看不出来注册的代码在哪,那我们就来研究下底层的源码。


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 里面做了什么事情。

一、初始化配置

1.1 初始化变量

injectEurekaConfiguration() 方法初始化了 Eureka 的一些变量,比如端口号、当前服务的访问路径、是否需要抓取注册表信息等等。


private static void injectEurekaConfiguration() throws UnknownHostException {    String myHostName = InetAddress.getLocalHost().getHostName();    String myServiceUrl = "http://" + myHostName + ":8080/v2/";    System.setProperty("eureka.region", "default");    System.setProperty("eureka.name", "eureka");    System.setProperty("eureka.vipAddress", "eureka.mydomain.net");    System.setProperty("eureka.port", "8080");    System.setProperty("eureka.preferSameZone", "false");    System.setProperty("eureka.shouldUseDns", "false");    System.setProperty("eureka.shouldFetchRegistry", "false");    System.setProperty("eureka.serviceUrl.defaultZone", myServiceUrl);    System.setProperty("eureka.serviceUrl.default.defaultZone", myServiceUrl);    System.setProperty("eureka.awsAccessId", "fake_aws_access_id");    System.setProperty("eureka.awsSecretKey", "fake_aws_secret_key");    System.setProperty("eureka.numberRegistrySyncRetries", "0");}
复制代码

1.2 获取配置文件配置

在这一行代码中,将配置文件 eureka-client.properties 中的配置读取后,放到了 EurekaInstanceConfig 中。这个 EurekaInstanceConfig 是用来初始化 applicationInfoManager 信息管理器的。


看下面代码,创建了一个 MyDataCenterInstanceConfig,其实就是创建了 EurekaInstanceConfig。


new MyDataCenterInstanceConfig()
复制代码


那 MyDataCenterInstanceConfig 和 EurekaInstanceConfig 是什么关系呢?



从类图关系中可以看到 MyDataCenterInstanceConfig 继承 PropertiesInstanceConfig 类,实现了 EurekaInstanceConfig 接口。这种接口之前专门讲过,通过接口来获取配置信息,类似这种方法 getXX()。


然后在 PropertiesInstanceConfig 类的构造函数调用了一个工具类,读取了配置文件 eureka-client.properties 中的值。这个隐藏的有点深啊!


Archaius1Utils.initConfig(CommonConstants.CONFIG_FILE_NAME);
复制代码

1.3 初始实例信息

主要就是构造出 instanceInfo 实例信息。这个里面的信息包含了第一步初始化变量中的配置信息。


InstanceInfo instanceInfo = new EurekaConfigBasedInstanceInfoProvider(instanceConfig).get();
复制代码


1.4 初始化实例信息管理器

就是将 instanceConfig 和 instanceInfo 交给实例信息管理器来管理。


applicationInfoManager = new ApplicationInfoManager(instanceConfig, instanceInfo);
复制代码

二、构造 EurekaClient

2.1 构造流程

构造 eurekaClient 的代码


eurekaClient = new DiscoveryClient(applicationInfoManager, clientConfig);
复制代码


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 的线程,


instanceInfoReplicator.start(40); // 40 s后执行
复制代码


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


instanceInfo.setIsDirty();
复制代码


(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 框架,国内用这个框架的不多,不用深究。请求的 url 为


http://localhost:8080/v2/apps/EUREKA



注册的方法里面发送了 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 时,Service 启动后,Eureka 很快就发现了 Service 的存在,如下图所示的控制台界面:



并不需要等待 40 s 才能注册到 Eureka,那这又是为什们呢?


下一篇,我们来看下 Eureka Server 是如何将 Eureka Client 发送过来的注册信息保存起来的。

发布于: 2021 年 11 月 04 日阅读数: 8
用户头像

用故事、大白话讲解Java、分布式、架构设计 2018.05.06 加入

公众号:「悟空聊架构」

评论

发布
暂无评论
Eureka 源码之客户端注册