Eureka是什么
Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运 行在 AWS(Amazon Web Services,亚马逊网络服务,亚马逊云)域中的中间层服务,以达到 负载均衡和中间层服务故障转移的目的。SpringCloud 将它集成在其子项目 spring-cloud-netflix 中,实现 SpringCloud 的服务发现功能。其实,Eureka 就是一个专门用于服务发现的服务器,一些服务注册到该服务器,而另一 些服务通过该服务器查找其所要调用执行的服务。可以充当服务发现服务器的组件很多,例 如 Zookeeper、Consul 等。
Eureka客户端
Eureka是有 client和server组成的,客户端和服务端通过HTTP协议进行通信。
客户端会会想服务端发送注册请求 请求参数主要是 服务名、ip+port等信息,并且有一下三个时机会发送注册请求
- 启动初始化的时候发起注册
- 发送心跳的时候如果服务端返回404,发起注册
- 客户端缓存实例租约信息变更了,发起注册
当应用启动之后 客户端会定期向服务端发送心跳信息(也就是告诉服务端自己还活着)默认是30s,当然这个时间可以通过的eureka.instance.eureka.lease-renewal-interval-in-seconds 进行调整,默认是30s。
在客户端启动的时候还初始化了其他的定时task包括了 客户端配置刷新,定时从Server端获取注册表信息(包括全量获取和增量获取)等相关操作,可以查看客户端源码进行了解
Eureka源码--客户端初始化
Eureka源码--注册&心跳
Euerka源码--客户端注册表更新
Euerka源码--客户端配置更新
Eureka服务端
Eureka是AP的,并且可以配置多个region,这个可以通过上面的架构图看出来。那Euerka Server对于客户端注册的实例是怎么处理的呢? Euerka服务端负责处理客户端请求(注册、心跳、状态更新、取消、删除、查询注册等相关操作),存储注册表信息,Euerka Server集群之间数据同步。 Euerak Server端存储注册表和最近变的(这两个是处理客户端获取注册表数据来源)
//注册信息
ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>> registry= new ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>()
//最近变更
ConcurrentLinkedQueue<RecentlyChangedItem> recentlyChangedQueue = new ConcurrentLinkedQueue<RecentlyChangedItem>()
EurekaServer 在处理客户端下载注册表的时候 分为了增量和全量下载,并且默认是从readOnlyCacheMap中进行下载的
private final ConcurrentMap<Key, Value> readOnlyCacheMap = new ConcurrentHashMap<Key, Value>();
private final LoadingCache<Key, Value> readWriteCacheMap;
为什么不直接从readWriteCacheMap中直接下载呢, 这样做的好处是 读写分离, 目的是为了保证集合迭代稳定性(当一个共享变量是集合并且存在对共享集合的高并发读取操作时, 在对共享集合进行写操作时,高并发的读操作也可以获得稳定的数据。这就是集合迭代的稳定性)
Eureka在处理注册等相关操作的时候 会加一些读写锁, 写入的时候加读锁,查询的时候加写锁。对于加了读锁,哪些加了写锁,这个可以在之前的源码中查看Eureka中读写锁问题 是为了解决共享集合迭代稳定性问题,共享集合指的是注册表和最近更新队列 recentlyChangeQueue
-
为什么续约操作不能加写锁
若续约操作添加了写锁,则意味着在对某一个client的续约进行处理时, server是无法处理其他client的任意读写操作。注意:续约操作是一个发生频率很高的操作
-
为什么续约操作不能加读锁
若续约操作添加了读锁,则意味着其他client的所有写操作请求均将被阻塞,续约操作是一个发生频率很高的操作
-
为什么写操作要加读锁
若添加写锁,则意味着任意写操作 均将对注册表的读/写操作处理处理阻塞。
那写操作添加读锁会出现什么问题?可能出现多个client读注册表中的同一个数据进行修改,但是这种情况不会出问题,因为注册表registry是JUC的
-
为什么读操作添加写锁
因为写操作添加了读锁,为了达到读写互斥,所以读操作添加了写锁
注意:仅增量下载添加了写锁,全量下载没有锁的
-
为什么全量下载没有添加写锁
全量下载操作的是注册表,增量下载操作的是recentlyChangeQueue
如果全量下载添加写锁,则意味着其在读取期间出现的所有续约操作将被阻塞
Eureka源码--服务端初始化
Eureka源码--处理客户端注册
Eureka源码--处理获取注册表
源码阅读细节
主要是考虑Eureka的设计,以及处理方式,例如读写分离,读写锁,全量增量下载,以及状态的设计(status 、overidenStatus)的等这些 在阅读的时候可以考虑为啥这样设计。