1.预备知识
1.1 Region 与 Zone
1.1.1 概念
Eureka 中具有 Region 与 Availability Zone(简称 AZ)概念,是云计算中的概念。
为了方便不同地理区域中用户的使用
,大型云服务提供商一般会根据用户需求量在不同的城市、省份、国家或洲创建不同的大型云计算机房。这些不同区域机房间一般是不能“内网连通”的。这些区域就称为一个 Region。
这里存在一个问题:同一 Region 机房是如何实现
同域容灾
的?为了增强容灾能力,在一个 Region 中又设置了不同的
Availability Zone
。这些 AZ 间实现了
内网连通
,且用户可以根据自己所在的具体位置自动选择同域中的不同 AZ。当用户所要访问的 AZ 出现问题后,系统会自动切换到其它可用的 AZ。
例如,AWS 将全球划分为了很多的 Region,例如美国东部区、美国西部区、欧洲区、非洲开普敦区、亚太区等。像 Eureka 系统架构图中的 us-east-1c、us-east-1d、us-east-1e 就是 us-east-1 这个 Region 中的 c、d、e 三个 AZ。
再如,阿里云在我国境内的 Region 有杭州、北京、深圳、青岛、香港等,境外 Region有亚太东南 1 区(新加坡)、亚太东南 2 区(悉尼)、亚太东北 1 区(东京)。
1.1.2 Eureka 中的 Region 与 Zone 配置
(1) 需求
假设某公司的服务器有 Beijing、Shanghai 等多个 Region。Beijing 这个 Region 中存在两个 AZ,分别是 bj-1 与 bj-2,每个 AZ 中有三台 Eureka Server。
h-1 与 h-2 两台主机提供的都是相同的 Servivce 服务,根据地理位置的不同,这两台主机分别注册到了距离自己最近的不同 AZ 的 Eureka Server。
但是因为Region中的AZ都是内网联通的,所以 整个Region中的每个Eureka Server数据都是一致的。
(2) 主机配置
Eureka配置:
-
A、Server AZ bj-1
说明:bj-1 这个 Eureka Server 的 AZ 集群中具有三台 Server,这三台 Server 的配置文件,除了 server.port 不同外,其它配置完全相同。
这三个 Server 的 IP 与端口分别为:ip00:8000,ip01:8001,ip02:8002,该三台主机属于同一个zone。
同时需要注意,defaultZone需要配置整个Region中所有的Eureka Server的地址
配置如下,只展示了一个,另外两个只是端口不同,可以看到是通过元数据配置zone的:
-
B、 Server AZ bj-2
说明:bj-2 这个 AZ 集群中具有三台 Server,这三台 Server 的配置文件,除了 server.port不同外,其它配置完全相同。
这三个 Server 的 IP 与端口分别为:ip10:8000,ip11:8001,ip12:8002,该三台主机属于同一个zone。
同时需要注意,defaultZone需要配置整个Region中所有的Eureka Server的地址
配置如下,也是只展示了一个,另外两个只是端口不同:
Zuul配置:
- C、 Zuul AZ bj-1
- D、Zuul AZ bj-2
Service配置:
这两个微服务名称是相同的,只是在不同的zone
- E、 Service AZ bj-1
- F、 Service AZ bj-2
可以通过以下配置,让Eureka Client优先选择相同zone的应用 如果当前region中的应用都不能用,可以用以下参数指定其他region
1.2 核心类
(1) InstanceInfo
简单来说,该类代表的就是注册表中的一个主机实例信息。每一个注册到Eureka Server的主机对应一个该类的实例。
两个时间戳:
后面分析源码会多次看到两个时间戳,先简单介绍一下:
- lastDirtyTimestamp:记录 instanceInfo 在
被修改的时间。Client 端
, instanceInfo 中任何信息被修改(数据中心信息、续约配置信息、状态等),都会更新该时间戳。这个字段只会在client端被更新
- lastUpdatedTimestamp:记录 instanceInfo 在
被修改的时间。Server 端
,server端通常只会修改 instanceInfo 的状态信息(我暂时只发现只修改了状态信息)。这个字段只会在server端被更新
两个状态:
instanceInfo中一共有两个状态overriddenStatus和status。
关于overriddenStatus,字面意思是覆盖状态,它是一个“外部”状态,可以由外部任何程序进行修改,例如之前演示服务平滑上下线,通过actuator修改服务器(主机实例)状态,修改的值其实就是overriddenStatus。而overriddenStatus并不代表服务器真正状态。
而status是真正的服务器状态,但是status的值是通过一套规则计算出来的,其中就需要overriddenStatus,后面分析源码会看到。
三个修改状态的方法:
-
setOverriddenStatus
该方法仅会在 Eureka Server 端被调用。(通过actuator修改状态UP/DOWN…由客户端发起请求,服务端处理请求的时候调用)
-
setStatusWithoutDirty
该方法仅会在 Eureka Server 端被调用,在修改 status 时不记录修改时间戳。
-
setStatus
该方法在 Eureka Client 端调用(定时检测配置更新的时候),用于设置 instance 的服务状态。
//InstanceInfo.java /** * Set the status for this instance. * * @param status status for this instance. * @return the prev status if a different status from the current was set, null otherwise */ public synchronized InstanceStatus setStatus(InstanceStatus status) { if (this.status != status) { InstanceStatus prev = this.status;//记录之前状态 this.status = status;//更新新的状态 setIsDirty();//设置dirty标志,脏数据标记,表示客户端和服务端之间数据不一致,需要同步 return prev;//返回之前的状态 } return null; } /** * Sets the dirty flag so that the instance information can be carried to * the discovery server on the next heartbeat. * 设置dirty标志,以便可以将实例信息传输到下一个heartbeat上的发现服务器。 * * 简单来说就是变成脏数据以后,客户端会发起请求和服务端进行数据同步,后面源码分析会看到 */ public synchronized void setIsDirty() { isInstanceInfoDirty = true; lastDirtyTimestamp = System.currentTimeMillis(); }
看下都有哪些状态:
- UP:对外提供服务的 启动状态,
Ribbon负载均衡的时候,只有当 status 的状态为 UP 时,才能被服务发现,进行服务调用
- OUT_OF_SERVICE:和DOWN效果一样,但一般是我们手动通过actuator修改状态,让其不提供服务,当然手动修改为DOWN也可以
- DOWN:一般不是手动修改,而是client和server心跳发生异常的时候,系统自动修改为DOWN
- STARTING:client启动过程、初始化过程中的状态,启动完成后状态就变了,一般维持的时间不会很长
- UNKNOWN:一般来说就是overriddenStatus的初始值
InstanceInfo重写了 equals()方法:
InstanceInfo重写了 equals()方法,将来两个InstanceInfo比较,主要看InstanceInfo的id:
(2) Application
也是注册表中的一部分,简单来说,注册表中同一个微服务的所有提供者的实例InstanceInfo信息都会维护在该类中,即一种微服务对应一个Application实例。
(3) Applications
这个就是我们说的注册表,维护的是整个从服务端获取的注册表信息。
说明一点:这个类专门为客户端使用的,实际上服务端的注册表数据结构,就是一个双层map,后面源码分析会看到。
(4) Jersey 框架
Eureka Client 与 Eureka Server 间的通信,及各个 Eureka Server 间的通信,使用的是 Jersey 框架完成的(
都是通过http协议进行通信的
)。
Jersey 框架是一个开源的 RESTful 框架。其功能与 SpringMVC 的相同。不同的是 SpringMVC 的处理器是 Controller,而 Jersey 的是 Resource。
2. Eureka Client 自动配置类的加载
PS:对Spring boot自动配置不熟的朋友,可以先去看一下关于SpringBoot自动配置的文章。我的博客里也有。
这里我们主要关注Eureka Client自动配置的时候都加载了哪些东西
入口从Eureka Client依赖开始:
找到对应的依赖的具体代码:
Eureka Client相关配置类的加载
关于@EnableConfigurationProperties注解:
可以看到对于@ConfigurationProperties的类的处理,可以直接在注解的value属性指定相应类名进行注册,或者通过 @Bean方法注册。EurekaClientAutoConfiguration是通过@Bean的方式:
- EurekaClientConfig
- EurekaInstanceConfig
Eureka Client 的创建
我们现在看我们最核心的EurekaClient的创建
问题1:
看下@ConditionalOnMissingBean注解的search属性:
对应有三种策略:
PS:当然判断的条件和Bean加载的顺序也有关,这个其实是SpringBoot自动配置的知识点,不是本篇重点。
问题2:
在讲Spring Cloud Config自动更新配置的时候,其实用过这个注解,这个注解可以让Spring容器刷新的时候,让当前实例重新生成新的实例。
问题3:
懒加载,简单来说就是如果不从Spring容器中获取这个bean实例,或者被其他实例引用,就不会对该bean进行初始化。
即只有用到的时候才会初始化。
问题4:
看一下@ConditionalOnMissingRefreshScope注解:
- Conditional注解:
- 看下具体的条件,OnMissingRefreshScopeCondition类:
回过头看@ConditionalOnRefreshScope注解对比以下,看到条件刚好是相反的:
所以这两个内置配置类不会同时起作用: