Eureka源码分析之Eureka-Client(一)
一、前言
SpringCloud-Eureka组件是基于NetFlix的Eureka封装而成,故本系列源码是从Netflix/eureka角度分析,后续再分析Spring-cloud是如何封装Netflix/eureka的。
Netflix/eureka项目地址:https://github.com/Netflix/eureka,本系列文章的分析是基于2.x版本进行的。
二、eureka组成
使用过eureka的都应该清楚,eureka组件由eureka-client和eureka-server组成。client即客户端,像服务Provider和服务Consumer都属于client,而server也就是注册中心。
本文先分析eureka-client的初始化过程,后续文章再分析eureka-server相关。
三、eureka类关系图
本文重点就是讲述EurekaClient是如何创建以及这几个类(接口)的定义。
四、类/接口定义
1.1 EurekaInstanceConfig
从类名可以看出是一个配置类,默认的实现是MyDataCenterInstanceConfig,类结构图如下:
EurekaInstanceConfig接口:定义一些列获取配置信息的getter接口,下面列出一些常用的getter方法:
1 getInstanceId()-实例ID,默认hostName:appName:port 2 getAppname()-appName 3 getAppGroupName()-分组名 4 isInstanceEnabledOnit-eureka-client初始化后是否马上启动,默认false 5 getLeaseRenewalIntervalInSeconds()-eureka-client向server续租频率,默认30s 6 getLeaseExpirationDurationInSeconds()-租约过期时间,默认90s 7 getHostName(boolean refresh)-hostName 8 getIpAddress()-ipAddress 9 getDataCenterInfo()-数据中心,默认为Name.MyOwn 10 getNamespace()-命名空间,默认eureka
1.2 AbstractInstanceConfig
抽象基类,主要定义了一些默认配置,下面列出部分,其余大家可以查看源码:
nameSpace=eureka
LEASE_EXPIRATION_DURATION_SECONDS = 90
LEASE_RENEWAL_INTERVAL_SECONDS = 30
INSTANCE_ENABLED_ON_INIT = false
1.3 PropertiesInstanceConfig
抽象类,主要功能是从属性配置文件eureka-client.properties读取配置信息,作为某些配置getter方法的返回值。
1.4 MyDataCenterInstanceConfig
EurekaInstanceConfig的默认实现,结构很简单,没什么逻辑。
@Singleton @ProvidedBy(MyDataCenterInstanceConfigProvider.class) public class MyDataCenterInstanceConfig extends PropertiesInstanceConfig implements EurekaInstanceConfig { public MyDataCenterInstanceConfig() { } public MyDataCenterInstanceConfig(String namespace) { super(namespace); } public MyDataCenterInstanceConfig(String namespace, DataCenterInfo dataCenterInfo) { super(namespace, dataCenterInfo); } }
总结,EurekaInstanceConfig只是一个配置类,通过默认配置,读取配置文件提供一系列配置信息用来初始化InstanceInfo,关于InstanceInfo接口下面会讲到。
2.1 InstanceInfo
有上述的EurekaInstanceConfig生成,这个类很重要,当然字段也非常多,建议大家去看看源码,字段也和EurekaInstanceConfig的字段大致相同,毕竟是由它提供配置信息创建的。
为什么说这个类很重要,因为eureka-client向eureka-server注册中心注册的时候,就是注册这个类。
这个类除了一些配置信息外,内部还定义了一个实例状态枚举InstanceStatus,另外这个类的创建采用了Builder设计模式。
public enum InstanceStatus { UP, // Ready to receive traffic DOWN, // Do not send traffic- healthcheck callback failed STARTING, // Just about starting- initializations to be done - do not // send traffic OUT_OF_SERVICE, // Intentionally shutdown for traffic UNKNOWN; public static InstanceStatus toEnum(String s) { if (s != null) { try { return InstanceStatus.valueOf(s.toUpperCase()); } catch (IllegalArgumentException e) { // ignore and fall through to unknown logger.debug("illegal argument supplied to InstanceStatus.valueOf: {}, defaulting to {}", s, UNKNOWN); } } return UNKNOWN; } }
3.1 ApplicationInfoManager
这个类由InstanceInfo和EurekaInstanceConfig创建的,用来管理应用信息,注意,管理的是自身的InstanceInfo,而不是从eureka-server拉取过来的InstanceInfo!构造方法如下:
@Inject public ApplicationInfoManager(EurekaInstanceConfig config, InstanceInfo instanceInfo, OptionalArgs optionalArgs) { this.config = config; this.instanceInfo = instanceInfo; this.listeners = new ConcurrentHashMap<String, StatusChangeListener>(); if (optionalArgs != null) { // 一般为NO_OP_MAPPER this.instanceStatusMapper = optionalArgs.getInstanceStatusMapper(); } else { this.instanceStatusMapper = NO_OP_MAPPER; } // Hack to allow for getInstance() to use the DI'd ApplicationInfoManager instance = this; }
上述的源码中,OptionalArgs用于提供InstanceStatusMapper,这个类的作用是传入一个InstanceStatus,根据一定规则返回一个InstanceStatus,一般会采用NO_OP_MAPPER,也就是不处理,所以不用放太多注意力在这个类(其实我也没搞懂这个类有什么用)。此外还有一个属性listeners=new ConcurrentHashMap<String, StatusChangeListener>,主要用于监听自身InstanceInfo状态的改变,来进行下一步操作。
public static interface StatusChangeListener { String getId(); void notify(StatusChangeEvent statusChangeEvent); }
此外,ApplicationInfoManager还提供了两个方法
refreshDataCenterInfoIfRequired()
refreshLeaseInfoIfRequired()
这个两个方法主要是根据配置信息(EurekaInstanceConfig)刷新当前自身InstanceInfo的IP&hostName和租约信息。这个目前我也不会用,因为正常操作下这些信息一般都是和配置信息一致的,个人估计和Spring-cloud-config这个组件有关,因为这个组件可以动态刷新配置信息。
4.1 EurekaClientConfig
这个接口也是配置接口,默认实现类为DefaultEurekaClientConfig。
这个配置和EurekaInstanceConfig不同之处在于,这个是用来配置EurekaClient的,里面大多配置都是和Eureka-Server交互相关,而EurekaInstanceConfig是用来配置InstanceInfo的,InstanceInfo主要提供注册信息到注册中心,如appName, hostName&ipAddress,租约信息等。下面给出部分配置属性:
getRegistryFetchIntervalSeconds() -- eureka-client从注册中心拉取InstanceInfo的频率,默认30s
getInstanceInfoReplicationIntervalSeconds() -- 自身InstanceInfo同步到注册中心的频率,默认30s
getInitialInstanceInfoReplicationIntervalSeconds() -- 自身InstanceInfo同步到注册中心时(第一次)的延迟时间(因为采用定时任务,所以是延迟执行时间),默认40s
shouldGZipContent() -- GZip压缩相关,默认true
getEurekaServerReadTimeoutSeconds() -- 从eureka-server读超时时间,8s
getEurekaServerConnectTimeoutSeconds() -- eureka-client连接到eureka-server的超时时间,默认5s
getBackupRegistryImpl() -- 备份注册中心,目前没有实现,不需太关注
shouldRegisterWithEureka() -- 是否注册到注册中心,默认true
shouldUnregisterOnShutdown() -- 自身服务关闭时是否发送下线信息到eureka-server,默认true
shouldPreferSameZoneEureka() -- 是否使用相同zone的eureka-server,默认true
shouldDisableDelta() -- 是否禁用增量拉取服务,默认false。意思是eureka-client从注册中心定时去拉取任务时,是全部都拉取过来,还是拉取最近增加的服务。
getRegion() -- 获取region,默认是us-east-1
getAvailabilityZones(String region) -- 返回一个String[],正常情况都是使用defaultZone。
shouldFilterOnlyUpInstances -- 过滤UP状态的实例,默认true
shouldFetchRegistry() -- 是否从注册中心拉取服务,默认true
EurekaTransportConfig getTransportConfig() -- 和HttpClient通讯相同
getHeartbeatExecutorThreadPoolSize() -- 心跳线程池大小,默认5?
getHeartbeatExecutorExponentialBackOffBound() -- 心跳定时任务执行失败的延迟重试时间,默认10s
getCacheRefreshExecutorThreadPoolSize() -- 缓存刷新线程池大小,默认5
getCacheRefreshExecutorExponentialBackOffBound() -- 缓存刷新定时任务失败的延迟重试时间,默认默认10s
4.2 DefaultEurekaClientConfig
EurekaClientConfig的默认实现,还是老规矩,从eureka-client.properties配置文件读取配置信息提供给香瓜你的getter方法。
另外有个EurekaTransportConfig属性,主要是用来配置eureka-client和eureka-server的网络传输相关配置,采用的硬编码配置。
5、总结
1、EurekaInstanceConfig和EurekClientConfig都是最顶层的接口,主要是通过硬编码+从配置文件读取配置信息来创建相关的配置类。
2、InstanceInfo主要用于提供自身服务信息注册到注册中心,而EurekaClientConfig主要是用来配置和注册中心交互通讯相关的配置,两者功能性不一样。
3、以上的一切一切都是为了用来创建本文的主角EurekaClient,为什么我不接下去说?因为这个创建过程太复杂,需要比较多的篇幅去描述,下文分析。