简单理解eureka的基本概念,实现原理和核心组件,基于自己对源码阅读的理解,可能会有些理解偏差。会比较少涉及源码解读,更多的是流程的认识,辅助阅读源码。
- eureka版本:1.10.17
基本概念
Eureka 分为server端和client端。
- server端负责实例的注册,实例信息的查询,实例的状态维护,实例的集群间同步。eureka的高可用相对于zookeeper来说,简单粗暴,就是依靠多副本间的信息同步,某台server实例挂了就挂了,整个server集群不会受到影响;信息同步可能会出现不一致,也同样不影响server集群的服务,一言以蔽之,(CAP理论中)eureka提供的是AP的特性,而zk提供的是CP的特性。
- client端没啥,就是注册,查询,维护心跳。
原理理解
Eureka server 原理理解
这里的原理当然都是围绕着注册来的,所以会先介绍数据结构,其次是实例如何注册以及如何维护管理。最后会介绍下集群的高可用背景下集群间的信息同步。
数据结构
eureka是基于application来做不同应用业务的隔离,所以顶层是application,每个application下面就会包含很多的应用实例:instance,这些instance本身会包含比如ip地址等信息,因为还涉及到实例的状态管理这些外部信息:lease(比如实例是否已经过期了),这些信息就存储在一个Map当中。
eureka server的所有功能就是围绕着这个数据结构来进行的了。
核心类:com.netflix.eureka.registry.AbstractInstanceRegistry
核心数据结构:ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>> registry,翻译下=> Map<ApplicationName, Map<InstanceId, Lease<InstanceInfo>
当然,eureka还做了一层缓存优化,以提高对外的查询速度(比如客户端来查询服务实例列表的时候),也可以理解为以上的map不会被外部直接访问。当有实例信息更新时,会把缓存清空掉。
缓存实现类:com.netflix.eureka.registry.ResponseCacheImpl
实例注册的实现
eureka的通信是基于http协议,具体默认实现是Jersey。
怎么实现呢,入口在哪?wireshark分析下客户端向服务器发送了哪些请求(这里我的注册中心端口是8081)。
所以入口就是:
POST /eureka/apps/{application-name}
方法入口:com.netflix.eureka.resources.ApplicationResource#addInstance
流程最终一路走到:com.netflix.eureka.registry.AbstractInstanceRegistry#register
注册完成
这里面当然会涉及到lease的创建,二级缓存CacheResponse的清空等操作。
实例的维护管理
如上图也可以看到,客户端会有心跳,然后最终还是通过AbstractInstanceRegistry来进行心跳的维护。
入口方法:com.netflix.eureka.resources.InstanceResource#renewLease
接下来,就是server端自身的状态维护了。毫无疑问,AbstractInstanceRegistry肯定会有一个定时任务EvictionTask,这个任务干什么的呢,自然就是捞出所有的注册的实例,然后判断状态,比如读取对应的Lease信息,超时无心跳的就干掉。
集群间信息同步
服务器集群间的每个实例对于其它实例来说,也是做为一个eureka client进行通行,当然信息间的同步都是基于推的方式俩俩间通信,即已有的服务实例把自身信息推送给其它新加入的实例,或者把自身有变动的信息推送告知其它实例。
这里简单提下,在服务集群中,每个服务实例都会保存所有其它服务实例列表,每个服务的实例会保存在PeerEurekaNode对象中,这个对象包含HttpReplicationClient用来实现peer通信。
直观看wireshark记录的请求,其中8081端口的是已经运行的实例,8071端口是新加入的实例。
- 接收同步信息
可以看到,新服务实例B启动后,同样地向某一台已有的服务实例A进行注册。(前面提到,Application是Eureka中用来区分业务应用的对象,因此所有服务实例都属于比如:EUREKASERVER这个应用。)
随后服务实例A就向B发送了一个batch请求,这个batch请求会包含所有已注册的实例的列表,每个注册的实例对象除了会包括自身的信息,还会包括当前实例向服务实例A执行的操作。简单理解,batch中的列表记录的是所有实例A受到的eureka client的请求,比如心跳,注册等。然后实例B获取到list后,根据信息dispatch不同操作。
http请求:POST /eureka/peerreplication/batch
方法入口:com.netflix.eureka.resources.PeerReplicationResource#batchReplication
- 推送同步信息
通过以上我们大致知道信息的同步过程,那服务实例A是怎么同步的呢。其实就是在客户端实例发起请求的时候,看文提到一个注册的核心类AbstractInstanceRegistry,这里eureka对这个类进行了继承装饰,叫PeerAwareInstanceRegistryImpl。顾名思义,这里的peer就是服务集群的其它实例。每次除了执行实例的注册等操作外,还会让peer感知到这次操作。
这里还有一个问题,难道每一次有客户端实例向服务器实例发起请求,服务器实例都要同步广播一次吗。
默认实现当然不是,不然也不会叫batch了。
PeerEurekaNode 对象中有一个成员叫batchingDispatcher,这个dispatcher里面有两个对象AcceptorExecutor和TaskExecutors,TaskExecutors会不断地拉取AcceptorExecutor的工作队列来执行batch任务(这里给个提示:com.netflix.eureka.util.batcher.AcceptorExecutor#requestWorkItems)。而AcceptorExecutor内部会间隔地把任务添加到工作队列。
springcloud是如何集成eureka server的
核心配置类:org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration
该配置类中会自动配置比如比较核心的EurekaClient,PeerAwareInstanceRegistry,还会创建一个jerseyApplication发布Eureka的各种对外Resource接口。
而EurekaClient本身是因为server starter这个包邮引了spring cloud eureka client这个包,这个包会导出EurekaClient组件实例。