Eureka简介:
Eureka是Spring Cloud Netflix微服务套件中的一部分,可以与Springboot构建的微服务很容易的整合起来。
Eureka包含了服务器端和客户端组件。
- 服务器端,也被称作是服务注册中心,用于提供服务的注册与发现。Eureka支持高可用的配置,当集群中有分片出现故障时,Eureka就会转入自动保护模式,它允许分片故障期间继续提供服务的发现和注册,当故障分片恢复正常时,集群中其他分片会把他们的状态再次同步回来。
- 客户端组件包含服务消费者与服务生产者。在应用程序运行时,Eureka客户端向注册中心注册自身提供的服务并周期性的发送心跳来更新它的服务租约。同时也可以从服务端查询当前注册的服务信息并把他们缓存到本地并周期性的刷新服务状态。
Eureka 服务的注册发现:
- 服务生产者启动时,向服务注册中心注册自己提供的服务
- 服务消费者启动时,在服务注册中心订阅自己所需要的服务
- 注册中心返回服务提供者的地址信息给消费者
- 消费者从提供者中调用服务
Eureka 的基本规则:
- 服务启动时会生成服务的基本信息对象InstanceInfo,然后在启动时会register到服务治理中心
- 注册完成后会从服务治理中心拉取所有的服务信息,缓存在本地
- 之后服务会被30s(可配置)发送一个心跳信息,续约服务
- 如果服务治理中心在90s内没有收到一个服务的续约,就会认为服务已经挂了,会把服务注册信息删掉
- 服务停止前,服务会主动发送一个停止请求,服务治理中心会删除这个服务的信息
- 如果Eureka Server收到的心跳包不足正常值的85%(可配置)就会进入自我保护模式,在这种模式下,Eureka Server不会删除任何服务信息
两个比较重要的配置
服务过期时间配置:eureka.instance.lease-expiration-duration-in-seconds
服务刷新时间配置:eureka.instance.lease-renewal-interval-in-seconds
Eureka 服务注册中心缓存:
Eureka用一个Map来保存所有服务及映射的机器信息
private final ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>> registry
= new ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>();
- 服务注册时,会把服务的信息写到这个
registry
中 - 服务从治理中心拉取服务列表信息时,不会从这个
registry
中拉取,而是从一个ResponseCache
中拉取,这样读写分离的原因应该是为了支持高并发
而ResponseCache
又分为了两个部分,一个是ReadWriteMap
,一个是ReadOnlyMap
ReadWriteMap
的数据是从registry
中来的,可以认为是registry
的缓存,当服务注册时,除了把信息写到registry
中外,还会让ReadWriteMap
主动过期,使得会去从registry
重新拉取数据ReadOnlyMap
的数据是从ReadWriteMap
来的,可以认为是ReadWriteMap
的缓存,当服务需要获取服务列表是,会直接取这个ReadOnlyMap
的数据,当这个数据不存在时,才会从ReadWriteMap
中更新ReadWriteMap
与registry
的数据是实时一致的,但是ReadWriteMap
与ReadOnlyMap
不是实时一致的- 有定时任务会定时从
ReadWriteMap
同步到ReadOnlyMap
,这个时间配置是:eureka.server.responseCacheUpdateInvervalMs
- EurekaServer内部有定时任务,每隔检查过期实例时间,扫描
Registry
里面过期的实例并删除,并且使对应的ReadWriteMap
缓存失效,这个时间是eureka.server.eviction-interval-timer-in-ms
Eureka 服务注册细节:
1)客户端的动作:
public void run() {
try {
discoveryClient.refreshInstanceInfo();
Long dirtyTimestamp = instanceInfo.isDirtyWithTime();
if (dirtyTimestamp != null) {
discoveryClient.register();
instanceInfo.unsetIsDirty(dirtyTimestamp);
}
} catch (Throwable t) {
logger.warn("There was a problem with the instance info replicator", t);
} finally {
Future next = scheduler.schedule(this, replicationIntervalSeconds, TimeUnit.SECONDS);
scheduledPeriodicRef.set(next);
}
}
- 在run方法中,会先去检测服务信息是否发生了变更,如果发生了,会调用 discoveryClient.register();方法重新注册,第一次的时候也会认为是发生了变更,从而发起第一次注册。
- 在finally块中,schedule了this,说明会在指定时间后再次执行这个方法
2)服务端的动作
private ConcurrentLinkedQueue<RecentlyChangedItem> recentlyChangedQueue =
new ConcurrentLinkedQueue<RecentlyChangedItem>();
- 首先会处理上面所说的
registry
以及它的缓存对象 - 其次会把这次的注册信息添加到一个“最近变更队列中”
- 然后会把注册的信息发送到其他的Eureka节点,发送的方式就是调用其他节点的
register
方法
Eureka 服务的续约:
在服务启动时,会创建一个后台心跳任务,定时去续约服务信息
scheduler.schedule(
new TimedSupervisorTask(
"heartbeat",
scheduler,
heartbeatExecutor,
renewalIntervalInSecs,
TimeUnit.SECONDS,
expBackOffBound,
new HeartbeatThread()
),
renewalIntervalInSecs, TimeUnit.SECONDS);
HeartbeatThread就是心跳线程,这里把它包装成了一个TimedSupervisorTask
HeartbeatThread:
private class HeartbeatThread implements Runnable {
public void run() {
/**
* renew()方法就是服务续约的方法,里面的逻辑就是向服务治理中心发送了一个http请求。
* 如果http请求返回的状态码是404,则会认为这个服务没有注册或者注册了但是已经失效,
* 因此会直接调用register()方法进行注册
*/
if (renew()) {
lastSuccessfulHeartbeatTimestamp = System.currentTimeMillis();
}
}
}
Eureka 服务的取消:
服务停止前会向服务治理中心发一个服务取消的请求,用于注销服务。收到服务注销的请求之后,服务治理中心会做以下操作
- 从registry中删除对应的服务信息
- 使ReadWriteMap缓存失效
- 将服务取消的信息加入到最近变更队列中