Eureka服务注册、服务续约源码解析、为何Eureka获取服务实例这么慢、自我保护模式

Eureka的一些概念
  1. Register——服务注册
    当Eureka Client向Eureka Server注册时,Eureka Client提供自身的元数据,比如IP地址、端口、运行状况指标的Url、主页地址等信息。

  2. Renew——服务续约
    Eureka Client 在默认的情况下会每隔30秒发送一次心跳来进 行服务续约。通过服务续约来告知Eureka Server该Eureka Client仍然可用,没有出现故障。正常情况下,如果Eureka Server在90秒内没有收到Eureka Client的心跳,Eureka Server会将Eureka Client实例从注册列表中删除。注意:官网建议不要更改服务续约的间隔时间。

  3. Fetch Registries——获取服务注册列表信息
    Eureka Client从Eureka Server获取服务注册表信息,并将其缓存在本地。Eureka Client会使用服务注册列表信息查找其他服务的信息,从而进行远程调用。该注册列表信息定时(每30秒)更新一次,每次返回注册列表信息可能与Eureka Client的缓存信息不同,Eureka Client会自己处理这些信息。如果由于某种原因导致注册列表信息不能及时匹配,EurekaClient会重新获取整个注册表信息。EurekaServer缓存了所有的服务注册列表信息,并将整个注册列表以及每个应用程序的信息进行了压缩,压缩内容和没有压缩的内容完全相同。EurekaClient和Eureka Server可以使用JSON和XML数据格式进行通信。在默认的情况下,Eureka Client使用JSON格式的方式来获取服务注册列表的信息。

  4. Cancel——服务下线
    Eureka Client在程序关闭时可以向Eureka Server发送下线请求。发送请求后,该客户端的实例信息将从Eureka Server 的服务注册列表中删除。该下线请求不会自动完成,需要在程序关闭时调用以下代码:
    DiscoveryManager .getInstance().shutdownComponent() ;

  5. Eviction——服务剔除
    在默认情况下,当Eureka Client连续90秒没有向Eureka Server发送服务续约(即心跳)时,EurekaServer会将该服务实例从服务注册列表删除,即服务剔除。

Eureka的高可用架构

在这里插入图片描述

在这个架构中有两个角色,即Eureka Server和Eureka Client。而EurekaClient又分为Applicaton Service和Application Client,即服务提供者和服务消费者。

每个区域有一个Eureka集群,并且每个区域至少有一个Eureka Server可以处理区域故障,以防服务器瘫痪。
Eureka Client向Eureka Server 注册,将自己的客户端信息提交给Eureka Server。然后,Eureka Client通过向Eureka Server 发送心跳(每30秒一次) 来续约服务。如果某个客户端不能持续续约,那么Eureka Server断定该客户端不可用,该不可用的客户端将在大约90秒后从EurekaServe服务注册列表中删除。服务注册列表信息和服务续约信息会被复制到集群中的每个EurekaServe节点。来自任何区域的EurekaClient都可以获取整个系统的服务注册列表信息。根据这些注册列表信息,Application Client可以远程调用Applicaton Service来消费服务。

Register服务注册

服务注册,即Eureka Client向Eureka Server提交自己的服务信息,包括IP地址、端口、ServiceId等信息。如果Eureka Client在配置文件中没有配置ServiceId,则默认为配置文件中配置的服务名,即${spring.application.name}的值。

当Eureka Client启动时,会将自身的服务信息发送到Eureka Server。这个过程其实非常简单,现在来从源码的角度分析服务注册的过程。

在工程的Maven 的依赖包下,找到eureka-client-1 .6.2.jar包。在com.netflix.discovery 包下有一个DiscoveryClient类,该类包含了Eureka Client向Eureka Server注册的相关方法。其中,DiscoveryClient 实现了EurekaClient 接口,并且它是一个单例模式,而EurekaClient 继承了LookupService接口。它们之间的关系如图5-4所示:
在这里插入图片描述

在DiscoveryClient类中有一个服务注册的方法register(),该方法通过Http请求向Eureka Server注册:
在这里插入图片描述

在DiscoveryClient类下继续追踪register()方法,这个方法被InstanceInfoReplicator类的run()方法调用,其中InstanceInfoReplicator实现了Runnable 接口,run()方法代码如下:
在这里插入图片描述

而InstanceInfoReplicator 类是在DiscoveryClient 初始化过程中使用的,其中有一个initScheduledTasks()方法,该方法主要开启了获取服务注册列表的信息。如果需要向EurekaServer注册,则开启注册,同时开启了定时任务向Eureka Server服务续约,具体代码如下:
在这里插入图片描述
在这里插入图片描述

再来跟踪Eureka server端的代码,在Maven的eureka-core:1.6.2的jar包下。打开com.netlix.eureka包,会发现有一个EurekaBootStrap 的类,BootStrapContext 类在程序启动时具有最先初始化的权限,代码如下:
在这里插入图片描述
在这里插入图片描述
其中,PeerAwareInstanceRegistryImpl 和PeerEurekaNodes 两个类从其命名上看,应该和服务注册以及Eureka Server高可用有关

先追踪PeerAwareInstanceRegstryImpl 类,在该类中有一个register(方法,该方法提供了服务注册,并且将服务注册后的信息同步到其他的Eureka Server服务中。代码如下:
在这里插入图片描述

点击其中的super.register(info, leaseDuration, isReplication)方法,进入其父类AbstractInstanceRegistry可以发现更多细节,注册列表的信息被保存在一个 Map中。

AbstractInstanceRegistry类的replicateToPeers(方法用于将注册列表信息同步到其他Eureka Server的其他Peers节点,追踪代码,发现该方法会循环遍历向所有的Peers 节点注册,最终执行类PeerEurekaNode的register()方法,该方法通过执行一个任务向其他节点同步该注册信息,代码如下:

在这里插入图片描述

经过一系列的源码追踪, 可以发现PeerAwareInstanceRegistryImpl类的register()方法实现了服务的注册,并且向其他Eureka Server的Peer节点同步了该注册信息,

那么register()方法被谁调用了呢?在前文中有关Eureka Client的分析中可以知道,Eureka Client是通过Http 来向Eureka Server注册的,那么Eureka Server 肯定会提供一个服务注册的API接口给Eureka Client 调用,PeerAwareInstanceRegistryImpl的register()方法最终肯定会被暴露的Http接口所调用

在IDEA开发工具中,同时按住“Alt" +鼠标左键(查看某个类被谁调用的快捷键),可以很快定位到ApplicationResource类的addInstance()方法,即服务注册的接口,其代码如下:
在这里插入图片描述

Renew服务续约

服务续约和服务注册非常相似,通过前文中的分析可以知道,服务注册在EurekaClient程序启动之后开启,并同时开启服务续约的定时任务。在eureka-client-1.6.2.jar的DiscoveryClient的类下有renew()方法,其代码如下:
在这里插入图片描述

另外,Eureka Server 的续约接口在eureka-core:1.6.2.jar 的com.netflix.eureka 包下的InstanceResource类下,接口方法为renewLease(),它是一个 RESTful API接口。其中有一个registry.renew()方法,即服务续约,代码如下:

在这里插入图片描述

服务续约有两个参数是可以配置的,即EurekaClient发送续约心跳的时间参数Eureka Server 在多长时间内没有收到心跳将实例剔除的时间参数。在默认情况下,这两个参数分别为30秒和90秒,官方的建议是不要修改,如果有特殊需求还是可以调整的,只需要分别在Eureka Client和Eureka Server的配置文件application.yml中加以下的配置:

eureka.instance.leaseRenewalIntervalInSeconds
eureka.instance.leaseExpirationdurationInSeconds
为什么Eureka Client获取服务实例这么慢
  1. Eureka Client的注册延迟
    EurekaClient启动之后,不是立即向EurekaServer注册的,而是有一个延迟向服务端注册的时间。通过跟踪源码,可以发现默认的延迟时间为40秒,源码在eureka-client-1.6.2.jar 的DefaultEurekaClientConfig类中,代码如下:

在这里插入图片描述

  1. Eureka Server的响应缓存
    Eureka Server 维护每30秒更新一次响应缓存, 可通过更改配置eureka.server.responseCacheUpdateIntervalMs来修改。所以即使是刚刚注册的实例,也不会立即出现在服务注册列表中。
  2. Eureka Client的缓存
    Eureka Client保留注册表信息的缓存。该缓存每30秒更新一次(如前所述)。因此,Eureka Client刷新本地缓存并发现其他新注册的实例可能需要30秒。
  3. LoadBalancer 的缓存
    Ribbon的负载平衡器从本地的EurekaClient获取服务注册列表信息。 Ribbon本身还维护了缓存,以避免每个请求都需要从EurekaClient获取服务注册列表。此缓存每30秒刷新一次(可由ribbon.ServerListRefreshInterval 配置),所以可能至少需要30秒的时间才能使用新注册的实例。

综上因素,一个新注册的实例,默认延迟40秒向服务注册中心注册,所以不能马上被Eureka Server发现。另外,刚注册的Eureka Client也不能立即被其他服务调用,原因是调用方由于各种缓存没有及时获取到最新的服务注册列表信息。

Eureka自我保护模式

当有一个新的EurekaServer出现时,它尝试从相邻Peer节点获取所有服务实例注册表信息。如果从相邻的Peer节点获取信息时出现了故障,Eureka Server 会尝试其他的Peer节点。如果EurekaServe能够成功获取所有的服务实例信息,则根据配置信息设置服务续约的阈值。在任何时间,如果Eureka Serve接收到的服务续约低于为该值配置的百分比(默认为15分钟内低于85%),则服务器开启自我保护模式,即不再剔除注册列表的信息。

这样做的好处在于,如果是EurekaServer自身的网络问题而导致EurekaClient无法续约,EurekaClient的注册列表信息不再被删除,也就是EurekaClient还可以被其他服务消费。
在默认情况下,Eureka Server的自我保护模式是开启的,如果需要关闭,则在配置文件添加以下代码:

eureka:
	server:
		enable-self-preservation: false
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值