一、Eureka原理
1、架构图
首先来看eureka的官方结构图
其中,在Eureka集群配置中,处于不同节点的Eureka通过Replicate进行数据同步;Application Server为服务提供者,Client为服务消费者。
2、基本原理
在Eureka响应的过程中,有三个角色,分别是Eureka、服务提供者、服务消费者;
- 在服务启动后,服务提供者向Eureka注册自己的信息,如调用地址、提供服务信息等
- Eureka为服务注册中心,向外暴露自己的地址,负责管理、记录服务提供者的信息,同时将符合要求的服务提供者地址列表返回服务消费者
- 服务消费者向Eureka订阅服务,表达自己的需求,然后得到服务提供者消息,远程调用即可
Eureka包含两个组件:Eureka Server和Eureka Client,作用如下:
- Eureka Client是一个Java客户端,主要用来简化和Eureka Server的交互
- Eureka Server提供服务发现的能力,各个微服务启动时,通过Eureka Client向Eureka Server注册自己的信息,Server储存该服务的信息
- 微服务启动后,周期性(默认为30s)地向Server发送心跳信息,以续约自己的信息,超时(默认为90s)未接受到心跳信息,Server将注销该服务节点
- 在Eureka Server设置注销某个服务提供者节点后,并将该服务向其他订阅者发布,订阅者更新本地缓存信息
- 在集群中,每个Eureka Server同时也是Eureka Client,互相注册完成服务注册表的消息同步,避免因为某个Eureka节点挂掉而导致整个微服务架构挂掉
- Eureka Client会缓存Server中的信息,在调用服务前,首先查看缓存是否存在相关信息
3、自我保护机制
由于在超时(默认90s)未接受到服务提供者的心跳续约信息后,Eureka Server就注销该实例,但往往由于微服务之间的跨进程调用,受网络通信情况影响较大,比如在微服务状态正常,但网络分区故障时,注销服务实例会让大部分微服务不可用,为避免这样二代情况,引进了Eureka的自我保护机制。
自我保护机制的原理是,当某个Eureka Server节点在短时间内丢失了过多的客户端节点时,该节点就进入自我保护机制,不在注销任何微服务实例,当网络故障恢复正常后,该节点自动退出自我保护机制。
自我保护机制根据自我保护阈值来区分是Eureka Server还是Eureka Client出了问题导致服务未能按时续约。
4、服务机制
一个服务按流程依次为服务注册,然后周期性地续约服务,若服务正常停止,则在停止服务之前向注册中心发送注销请求,若为非正常停止,则不会发送,该服务由Eureka Server主动剔除。
4.1、服务注册机制
在注册中心接受到register请求后:
- 保存服务信息,将服务信息保存到registry中;
- 更新队列,将此事件添加到更新队列中;
- 清空二级缓存,保证数据一致性;
- 更新阈值,供剔除服务使用;
- 同步服务信息,将此事件同步至其他Eureka Server节点。
4.2、服务续约机制
在注册中心接受到续约请求后,更新服务对象的最近续约时间,然后同步服务信息,将此事件同步至其他Eureka Server节点。
剔除服务之前会首先判断服务是否过期,是否过期的条件之一就是最近续约时间和当前时间差值是否大于阈值。
4.3、服务注销机制
在注册中心接受到cancel请求后:
- 删除服务信息,将服务信息从registry中删除;
- 更新队列,将此事件添加到更新队列中;
- 清空二级缓存,保证数据一致性;
- 更新阈值,供剔除服务使用;
- 同步服务信息,将此事件同步至其他Eureka Server节点。
4.4、服务剔除机制
服务剔除包括三个步骤,首先判断是否满足服务剔除的条件,然后找出过期的服务,最后执行服务的剔除。
判断是否满足服务剔除的条件——主要看Eureka是否开启自我保护机制
关闭自我保护机制的情况下,直接进入下一步;
开启自我保护机制的情况下,只有实际续约数大于自我保护阈值,才会进入服务剔除流程。实际续约数大于自我保护阈值,说明大部分服务可用,判定是Eureka Client出了问题,反之,判定是Eureka Server出了问题,其中自我保护阈值=服务总数*(60s / 客户端续约间隔)*自我保护阈值因子。
找出过期的服务
遍历所有服务,判断最近续约时间距离当前时间差值大于设定的阈值,就标记为过期,并将所有过期的服务保存到集合中。
剔除服务
遍历所有过期服务,通过洗牌算法确保每次都公平地选择出要剔除的服务,最后进行剔除。
二、Eureka实现原理
2.1、Register
该接口会在服务提供者启动时被调用来实现服务注册,同时,在提供者的服务状态发生变化时,也会调用来更新服务状态;
接口操作:1、ApplicationResource类接受Http服务请求,调用PeerAwareInstanceRegistryImpl的register方法;2、PeerAwareInstanceRegistryImpl完成服务注册后,调用replicateToPeers向其他Eureka Server节点(Peer)做状态同步(异步操作方式);
注册的服务列表保存在一个嵌套的hash map中,第一层hash map的key是app name,即应用名称;第二层hash map的key是instance name,即实例名称。
2.2、Renew
服务续约操作由服务提供者定期调用,类似于heartBeat,向Server续约,避免服务被剔除;
接口实现与Register基本一致,首先更新自身状态,再同步到其他Peer。
2.3、Cancel
服务下线一般在服务提供者shut down时调用,用来把自身服务从Eureka Server中删除,以防客户端调用不存在的服务。
2.4、Fetch Registries
Fetch Registries由服务消费者调用,用来获取Eureka Server上注册的服务;
为提高性能,服务注册列表会缓存一份,同时每30s更新一次缓存。
2.5、Eviction
失效服务剔除用来定期(默认为60s)剔除Server中检测失效的服务,具体剔除的流程参考服务剔除机制。
2.6、Replicate
实现Server之间的信息同步,服务提供者仅需通知其中一个Server就能保证状态会在所有的Eureka Server中得到更新;
实现方法就是接受请求的节点,把请求再次转发到其他的Eureka Server,调用相同的接口,传入相同的参数,除了标记isReplication=true,从而避免重复的replicate;
节点之间的状态同步是采用异步的方式完成,所以不保证节点之间的状态一定是一致的,但基本保证最终状态是一致的;
这种异步的同步方式,实际应用中,业务上完全可以接受(比如,消费者一侧一般会实现错误重试和负载均衡机制),所以并不严格要求节点之间的状态强一致。
2.7、New Server Initialize
一个新的Eureka Server节点加入,在启动时将自己当作服务消费者,从其他节点获得所有服务的注册信息,然后对每个服务,在本节点执行Register,isReplication=true,从而完成初始化。
2.8、两层Hash Map
三、服务注册中心的选择
1、CAP原则
CAP定理:在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance),三者不可同时获得。
- 一致性:在分布式系统中,所有数据备份在同一时刻是否为同样的值
- 可用性:集群中一部分节点故障后,该集群还能否正常响应客户端的读写请求
- 分区容错性:一个节点故障挂掉后,不影响其他节点的正常使用,即高可用性
CAP理论说明在一个分布式系统中,最多同时实现其中的两点,而由于当前的网络硬件必然会出现延迟丢包的问题,所以分区容错性是必要的,所以只能在一致性和可用性中间权衡。
2、注册中心的选择
目前主流的注册中心有Eureka、Zookeeper、consul等,各自的侧重点不同。
2.1、Zookeeper保证CP
Zookeeper在master节点因为网络故障而与其他节点失去联系后,剩余节点会重新进行leader节点的选举,而在选举期间(30~120s),整个zk集群都是不可用的,虽然服务最终也能恢复,但是漫长的选举时间导致注册中心经常长期不可用却是不能容忍的。
2.2、Consul保证CP
Consul强一致性带来的是:服务注册要比Eureka慢,因为consul的raft协议要求必须过半数的节点都写入成功才认为是注册成功;当Leader节点挂掉后,重新选举期间整个consul不可用,牺牲了集群的可用性。
2.3、Eureka保证AP
Eureka在设计时优先保证可用性,其中各个节点都是平等的,几个节点挂掉,剩余节点仍能提供注册和查询服务,只要有一台Eureka还在,就能保证服务可用(保证可用性),但不保证查询到的信息时最新的(不保证强一致性);除此之外,由于自我保护机制不再剔除服务实例的节点,仍能接受新服务的注册和查询请求,但不会被同步到其他节点上。
同时,Eureka还提供客户端(Eureka客户端程序负责向外提供注册与发现服务接口)缓存功能,所以极端情况下,客户端无法访问任何一个Server节点,消费者仍能通过客户端查询与获取注册服务信息。
因此,Eureka可以很好地应对因网络故障导致部分节点失联的情况,尽可能地保证整个集群的可用性。