服务注册
Service Provider启动时会将服务信息(InstanceInfo)发送给eureka server,
eureka server接收到之后会写入registry中,服务注册默认过期时间DEFAULT_DURATION_IN_SECS = 90秒。
InstanceInfo写入到本地registry之后,然后同步给其他peer节点,对应方法com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#replicateToPeers。
写入本地registry
服务信息(InstanceInfo)保存在Lease中,写入本地registry对应方法com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#register,Lease统一保存在内存的ConcurrentHashMap中,
在服务注册过程中,首先加个读锁,然后从registry中判断该Lease是否已存在,
如果已存在则比较lastDirtyTimestamp时间戳,取二者最大的服务信息,避免发生数据覆盖
、
通过读锁并且 registry 的读取和写入不是原子的,那么在并发时其实是有可能发生数据覆盖的,如果发生数据覆盖岂不是有问题了?
其实针对这个问题,eureka的处理方式是没有问题的,该方法并发时,针对InstanceInfo Lease的构造,二者的信息是基本一致的,因为registrationTimestamp取的就是当前时间,所以并发的数据不会产生问题。
同步给其他peer
InstanceInfo写入到本地registry之后,然后同步给其他peer节点,对应方法com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#replicateToPeers。
如果当前节点接收到的InstanceInfo本身就是另一个节点同步来的,则不会继续同步给其他节点,避免形成“广播效应”;
InstanceInfo同步时会排除当前节点。
InstanceInfo的状态有依以下几种:Heartbeat, Register, Cancel, StatusUpdate,DeleteStatusOverride,默认情况下同步操作时批量异步执行的,同步请求首先缓存到Map中,key为requestType+appName+id,然后由发送线程将请求发送到peer节点。
Peer之间的状态是采用异步的方式同步的,所以不保证节点间的状态一定是一致的,不过基本能保证最终状态是一致的
合服务发现的场景,实际上也并不需要节点间的状态强一致。在一段时间内(比如30秒),节点A比节点B多一个服务实例或少一个服务实例,在业务上也是完全可以接受的(Service Consumer侧一般也会实现错误重试和负载均衡机制)。所以按照C(一致性)A(高可用)P(分区容错)理论,Eureka的选择就是放弃C,选择AP。
如果同步过程中,出现了异常怎么办呢,这时会根据异常信息做对应的处理,如果是读取超时或者网络连接异常,则稍后重试;如果其他异常则打印错误日志不再后续处理。
服务续约
Renew(服务续约)操作由Service Provider定期调用,类似于heartbeat。
主要是用来告诉Eureka Server Service Provider还活着,避免服务被剔除掉。
renew接口实现方式和register基本一致:首先更新自身状态,再同步到其它Peer,服务续约也就是把过期时间设置为当前时间加上duration的值。
注意:服务注册如果InstanceInfo不存在则加入,存在则更新;而服务预约只是进行更新,如果InstanceInfo不存在直接返回false。
服务下线
Cancel(服务下线)一般在Service Provider shutdown的时候调用,用来把自身的服务从Eureka Server中删除,以防客户端调用不存在的服务,eureka从本地”删除“(设置为删除状态)之后会同步给其他peer,对应方法com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#cancel。