Eureka是Netflix开源的服务注册和发现服务。SpringCloudEureka基于Eureka进行二次封装,增加了更加人性化的UI,更加易用。但是由于Eureka本身有很多缓存,导致服务状态更新滞后。最常见的情况是服务下线后状态没有及时更新,服务消费者调用下线服务,导致请求失败。本文基于SpringCloudEureka1.4.4.RELEASE,在默认region和zone的前提下,介绍Eureka的缓存机制。1.AP特性从CAP理论来看,Eureka是一个优先考虑可用性(A)和分区容错性(P),不保证强一致性(C),只保证最终一致性的AP系统。因此,在架构上设计了更多的缓存。(Eureka高可用架构)2.服务状态Eureka服务状态枚举类:com.netflix.appinfo.InstanceInfo.InstanceStatus3.EurekaServer在Eureka高可用架构中,EurekaServer也可以作为客户端向其他服务器注册,多个节点相互注册形成一个Eureka集群,集群之间被视为对等体。EurekaClient在向Server注册、续约、更新状态时,接受节点更新自己的服务注册信息,然后一一同步给其他对等节点。【注意】如果server-A向server-B节点单向注册,server-A将server-B视为对等节点,server-A接收到的数据会同步到server-B,但是server-B接收到的数据服务器B不会同步到服务器A。1.缓存机制EurekaServer中有三个变量:(registry,readWriteCacheMap,readOnlyCacheMap)保存服务注册信息。默认定时任务会每30s同步readWriteCacheMap到readOnlyCacheMap,每60s清理超过90s未更新的节点,EurekaClient每60s30s从readOnlyCacheMap更新服务注册信息,UI从registry更新服务注册信息.三级缓存:缓存相关配置:关键类:4.EurekaClientEurekaClient有两个角色:服务提供者和服务消费者。作为服务消费者,一般配合Ribbon或Feign使用(Feign内部使用Ribbon)。EurekaClient启动后,立即向Server注册为服务提供者,默认每30s更新一次;作为服务消费者,立即全量更新服务注册信息到Server,默认每30s增量更新一次服务注册信息;Ribbon延迟1秒获取Client使用的服务注册信息。默认情况下,服务注册信息每30秒更新一次,只保存状态为UP的服务。二级缓存:缓存相关配置:关键类别:5.默认配置下服务消费者的最大感知时间考虑了以下几种情况:0s时,服务不通知EurekaClient,直接下线;在29s时,第一个过期支票驱逐不超过90s;89s,第二次expiredcheckevict没有超过90s;在149s,第三次expiredcheckevict超过90s没有更新,所以服务实例从registry和readWriteCacheMap中删除;179s,定时任务从readWriteCacheMap更新为readOnlyCacheMap;209s,EurekaClient从EurekaServer的readOnlyCacheMap更新;在239秒,功能区从Eureka客户端更新。因此,在极端情况下,服务消费者最长的感知时间最多会接近240s。6.对策服务注册中心选择使用Eureka时,已经接受了其优先保证可用性(A)和分区容错(P),不保证强一致性(C)。如果需要优先保证强一致性(C),应该考虑使用ZooKeeper等CP系统作为服务注册中心。在分布式系统中,一般配置多个节点,单节点服务在线状态更新滞后没有影响。这里主要考虑服务下线后状态更新滞后的对策。1、EurekaServer缩短了readOnlyCacheMap的更新周期。缩短cron任务周期可减少滞后时间。eureka.server.responsecCacheUpdateIntervalMs:10000#EurekaServerreadOnlyCacheMap更新周期关闭readOnlyCacheMap。中小型系统可以考虑这种方案。EurekaClient直接从readWriteCacheMap更新服务注册信息。eureka.server.useReadOnlyResponseCache:false#是否使用readOnlyCacheMap2。EurekaClient服务消费者使用容错机制。比如SpringCloudRetry和Hystrix、Ribbon、Feign、Zuul都可以配置Retry。服务消费者访问离线节点时,通常会上报ConnectTimeout。这时可以通过Retry机制重试下一个节点。服务消费者缩短了更新周期。EurekaClient和Ribbon的二级缓存影响状态更新。缩短这两个定时任务周期可以减少滞后时间,比如配置:eureka.client.registryFetchIntervalSeconds:5#EurekaClientupdatecycleribbon.ServerListRefreshInterval:2000服务商保证服务正常下线。服务离线时使用kill或kill-15命令,避免使用kill-9命令。kill或kill-15命令杀进程时,会触发EurekaClient的shutdown()方法,主动删除server的registry和readWriteCacheMap中的注册信息,不必依赖Server的evict清除。服务商延迟下线。服务下线前,先调用接口,使EurekaServer中保存的服务状态为DOWN或OUT_OF_SERVICE,然后下线。两者的时间差是由缓存机制和配置决定的。比如默认调用接口后,延迟90s再下线。服务消费者不会调用离线服务实例。7、网关实现服务下线的实时感知在软件工程中,没有中间层解决不了的问题,网关就是介于服务提供者和服务消费者之间的中间层。以SpringCloudZuul网关为例,网关将服务注册信息保存为EurekaClient,服务消费者通过网关将请求转发给服务提供者。它只需要在服务提供者下线时通知网关它在自己保存的服务列表中即可。禁用该服务。为了保持网关的独立性,可以实现一个独立的服务来接收下线通知和协调网关集群。【本文为栏目机构易新科技原创文章,微信公众号“易新科技(id:CE_TECH)”点击此处查看作者更多好文
