Spring Cloud Eureka 详解
1). Eureka 的核心类
1. InstanceInfo
InstanceInfo 封装了服务实例信息
public class InstanceInfo {
....
// 实例ID
private volatile String instanceId;
// 应用名
private volatile String appName;
// 应用所属群组
private volatile String appGroupName;
// ip地址
private volatile String ipAddr;
// 默认端口号 7001
public static final int DEFAULT_PORT = 7001;
// 端口号
private volatile int port = DEFAULT_PORT;
//默认 https 端口号
public static final int DEFAULT_SECURE_PORT = 7002;
// https 端口号
private volatile int securePort = DEFAULT_SECURE_PORT;
// 应用实例的首页URL
private volatile String homePageUrl;
// 应用实例状态页URL
private volatile String statusPageUrl;
// 应用实例健康页URL
private volatile String healthCheckUrl;
// 应用实例健康页的 HTTPS URL
private volatile String secureHealthCheckUrl;
// 虚拟地址
private volatile String vipAddress;
// https 虚拟地址
private volatile String secureVipAddress;
// Defaults to US 默认为1
private volatile int countryId = DEFAULT_COUNTRY_ID;
// dataCenter 信息 ,NetFlix 或者 Amazon 或者 MyOwn
private volatile DataCenterInfo dataCenterInfo;
// 主机名称
private volatile String hostName;
//实例状态,UP ,DOWN ,STARTING ,OUT_OF_SERVICE,UNKNOWN
// 其中 OUT_OF_SERVICE 标识服务停止,处于这个状态的服务不会被路由到,经常用于升级部署的场景。
private volatile InstanceStatus status = InstanceStatus.UP;
// 外界需要强制覆盖的状态值, 默认为UNKNOWN
private volatile InstanceStatus overriddenStatus = InstanceStatus.UNKNOWN;
// 租约信息
private volatile LeaseInfo leaseInfo;
// 首先标识是否是discoveryServer ,其实标识该discoveryServer是否是响应你请求的实例
private volatile Boolean isCoordinatingDiscoveryServer = Boolean.FALSE;
// 应用实例的元数据信息
private volatile Map<String, String> metadata;
// 状态信息更新的时间
private volatile Long lastUpdatedTimestamp;
// 实例信息最新的过期时间 , 实例信息最新的过期时间
private volatile Long lastDirtyTimestamp;
// 标识Eureka Server 对该实例的操作,包括ADDED,MODIFIED,DELETED 这三类 。
private volatile ActionType actionType;
// AWS的autoscaling的名称
private volatile String asgName;
}
可以看到InstanceInfo 里既有metadata ,也有dataCenterInfo ,还有一个比较重要的LeaseInfo ,用来标识应用实例的租约信息。
2. LeaseInfo
Eureka 使用LeaseInfo (com.netflix.appinfo.LeaseInfo.java) 来标识应用实例的租约的信息。
public class LeaseInfo {
public static final int DEFAULT_LEASE_RENEWAL_INTERVAL = 30;
public static final int DEFAULT_LEASE_DURATION = 90;
// Client settings(客户端配置)续约的时间间隔
private int renewalIntervalInSecs=DEFAULT_LEASE_RENEWAL_INTERVAL;
// 需要设定的租约的有效时长
private int durationInSecs = DEFAULT_LEASE_DURATION;
// Server populated (服务端填充)
// server 端设置的该租约的第一次注册时间
private long registrationTimestamp;
// server 端设置的该租约的最后一次续约时间
private long lastRenewalTimestamp;
// server端设置的该租约被删除的时间
private long evictionTimestamp;
// server端设置的该服务实例标记为UP的时间
private long serviceUpTimestamp;
}
这些参数主要用于标识应用实例的心跳情况,比如约定的心跳周期,租约有效期,最近一次续约时间。
3. ServieceInstance
ServiceInstance 是Spring Cloud 对 service discovery 的实例信息的抽象接口,约定了服务发现的实例有哪些通用的信息。
public interface ServiceInstance {
/**
* 获取服务ID
*/
String getServiceId();
/**
* 服务实例的 host
*/
String getHost();
/**
* 服务实例的端口
*/
int getPort();
/**
* 服务是否开启 https
*/
boolean isSecure();
/**
*实例的uri地址
*/
URI getUri();
/**
* 服务实例的元数据
*/
Map<String, String> getMetadata();
/**
* 实例的 scheme
*/
default String getScheme() {
return null;
}
}
2). 服务的核心操作
对于服务发现来说,关于服务实例的主要操作又几个重要的操作:
-
服务注册(register)
-
服务下线(cancel)
-
服务租约(renew)
-
服务剔除(evict)
围绕这几个功能,Eureka 设计了几个核心操作类:
com.netflix.eureka.lease.LeaseManager
com.netflix.discovery.shared.LookupService
com.netflix.eureka.registry.InstanceRegistry
com.netflix.eureka.registry.AbstractInstanceRegistry
com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl
Spring Cloud Eureka 在netflix 的基础上,抽象和定义了如下几个核心类:
org.springframework.cloud.netflix.eureka.server.InstanceRegistry
org.springframework.cloud.client.serviceregistry.ServiceRegistry
org.springframework.cloud.netflix.eureka.serviceregistry.EurekaServiceRegistry
org.springframework.cloud.netflix.eureka.serviceregistry.EurekaRegistration
org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration
org.springframework.cloud.netflix.eureka.EurekaClientConfigBean
org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean
LeaseManager 以及LookupService 是eureka关于服务发现操作相关的接口类,Leasemanager 定义了服务写操作相关的方法,LookupService定义了查询的操作方法。
1. LeaseManager
public interface LeaseManager<T> {
/*
注册服务实例
*/
void register(T r, int leaseDuration, boolean isReplication);
/*
删除服务实例
*/
boolean cancel(String appName, String id, boolean isReplication);
/*
与Eureka server 进行心跳检测 , 维持租约
*/
boolean renew(String appName, String id, boolean isReplication);
/**
去除租约过期的服务实例
*/
void evict();
}
具体实现在: AbstractInstanceRegistry
public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {
try {
// 上读锁
read.lock();
// 判断实例是否已经注册
Map<String, Lease<InstanceInfo>> gMap = registry.get(registrant.getAppName());
REGISTER.increment(isReplication);
if (gMap == null) {
final ConcurrentHashMap<String, Lease<InstanceInfo>> gNewMap = new ConcurrentHashMap<String, Lease<InstanceInfo>>();
gMap = registry.putIfAbsent(registrant.getAppName(), gNewMap);
if (gMap == null) {
gMap = gNewMap;
}
}
Lease<InstanceInfo> existingLease = gMap.get(registrant.getId());
// 如果已经存在租约 , 保存最后的过期时间
if (existingLease != null && (existingLease.getHolder() != null)) {
Long existingLastDirtyTimestamp = existingLease.getHolder().getLastDirtyTimestamp();
Long registrationLastDirtyTimestamp = registrant.getLastDirtyTimestamp();
logger.debug("Existing lease found (existing={}, provided={}", existingLastDirtyTimestamp, registrationLastDirtyTimestamp);
// 已存在的实例过期时间 大于 注册的过期时间 ,将用本地的副本
if (existingLastDirtyTimestamp > registrationLastDirtyTimestamp) {
logger.warn("There is an existing lease and the existing lease's dirty time