spring cloud之 eureka原理及其配置

 

Spring Cloud针对服务注册与发现,进行了一层抽象,并提供了三种实现: Eureka(支持得最好)、Consul、Zookeeper。

由于项目中使用到了Eureka,所以对Eureka有了一个深入的了解.

1.Eureka概述

        Eureka 是 Netflix 开源的服务注册发现组件,服务端通过 REST 协议暴露服务,提供应用服务的注册和发现的功能。 
我们将所有的Eureka服务称为实例(instance), Eureka服务又分为两类:Eureka Server(服务端)和Eureka Client (客户端);
Eureka-Client 又分为Application Provider 和Application Consumer,
Application Provider :服务提供者,内嵌 Eureka-Client ,它向 Eureka-Server 注册自身服务、续约、下线等操作 .
Application Consumer :服务消费者,内嵌 Eureka-Client ,它从 Eureka-Server 获取服务列表,分为全量获取和增量。

        

这里需要理解的是服务提供者和服务消费者只是代表了一个角色,一个Eureka-Client既可以当作服务提供者向外提供服务,又可以当作服务消费者去消费其他的服务.

在springcloud中Eureka的配置主要分为三种类型:

  • eureka.instance.*: Eureka实例注册配置项,这些是无论Eureka作为Server,Client都需要的公共配置项。对应的配置类为org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean。
  • eureka.server.*: Eureka做为Eureka Server才需要配置的选项,即使用eureka做注册中心时需要配置的选项。对应的配置类为org.springframework.cloud.netflix.eureka.EurekaClientConfigBean。
  • eureka.client.*: Eureka做为Eureka Client才需要配置的选项,即服务需要向注册中心注册或使用注册中心中的服务时需要配置的选项。对应的配置类为org.springframework.cloud.netflix.eureka.server.EurekaServerConfigBean

2.Eureka的架构

2.1 服务首次注册

  1. 服务提供者在启动时会向Eureka Server注册自身信息(包括服务名(Eureka Service Id)、ip地址、port、心跳周期等),EurekaServer获取注册请求,将服务实例信息存入到读写缓存中.
  2. 服务消费者根据Eureka 的service id从 Eureka Server获取要访问服务的注册信息,第一次请求时会一次性拉取对应Eureka server中全部服务的实例信息.
  3. Eureka Server收到请求后,首先会在只读缓存查找服务信息,如果找到则直接返回,否则去查找读写缓存,如果找到将其存入到只读缓存中,然后返回查找结果.这一点有点类似于redis作缓存服务器时的用法.
  4. 服务消费者拉取注册信息后,将服务实例信息缓存到本地,在使用时根据负载均衡算法从N个服务集群中选取一个服务并向这个服务发起请求

client客户端常用配置信息:

#服务service-id
spring:
  application:
    name: ek-provider
#服务端口
server:
  port: 8100
#实例配置
eureka:
  instance:
    #服务续约任务(心跳)的调用间隔,单位:秒,默认30s
    leaseRenewalIntervalInSeconds: 5
    #定义服务失效的时间,单位:秒,默认90s
    leaseExpirationDurationInSeconds: 10
  client:
    serviceUrl:
      #注册中心地址
      defaultZone: http://localhost:8000/eureka/
    
#日志
logging:
  level:
    com.netflix: DEBUG

除此之外,还有以下常用配置可选:

 

续约/心跳/下线:

  1. 服务提供者会定时向Eureka Server发送心跳,默认30s
  2. Eureka Server收到心跳后,会更新对应的服务实例信息,如果服务的状态有变化则将实例的变化加入到”最近租约变更记录队列”中
  3. Eureka Server有个实例过期清理定时器,如果在指定时间内没有收到心跳(默认90s),则认为服务已经下线,会从读写缓存中移除此实例,将并此变化更新“最近租约变更记录队列”。通常建议将存活的最长时间设置为3个心跳
  4. 服务消费者也有个定时任务会定时去更新服务实例信息(默认30s),第一次全量拉取服务实例,会读取所有的实例信息; 之后使用增量拉取服务实例信息,Eureka Server根据”最近租约变更记录队列”,告诉请求方哪些服务实例有变化,只返回变化的实例信息。客户端根据返回的增量信息更新本地缓存。我们也可以禁用增量获取服务实例实例,这样每次会全量获取服务实例信息

      服务实例下线,分为两种:

  • 主动下线,在服务实例结束前,主动通知Eureka Server,在默认配置下,此时服务消费者最长在30s左右知道服务提供者已经下线.
  • 被动下线,服务进度崩溃/网络异常,此时服务消费者最长在(3次心跳+一次刷新频率30s)(120s左右)内知道服务提供者已经下线
# EurekaClientConfig,重在 Eureka-Client,例如, 连接的 Eureka-Server 的地址、获取服务提供者列表的频率、注册自身为服务提供者的频率等等 
## 服务发现相关的配置
# 是否从 Eureka-Server 拉取服务实例注册信息,默认值为true
eureka.client.fetch-registry: true
# 从 Eureka-Server 拉取注册服务实例信息频率,默认:30 秒
eureka.client.registry-fetch-interval-seconds: 30 

# 是否禁用增量获取服务实例注册信息
eureka.disableDelta: false
# EurekaInstanceConfig,重在服务实例,例如,等等。此处应用指的是,Application Consumer 和 Application Provider。
# 服务实例配置
# 心跳,租约续约频率,单位:秒
eureka.instance.lease-renewal-interval-in-seconds: 30
#  eureka server多久没有收到心跳,则表示对应的实例过期,单位:秒。
eureka.instance.lease-expiration-duration-in-seconds: : 90s
# Eureka Server端服务实例租约过期定时任务执行频率
eureka.server.eviction-interval-timer-in-ms: 60s 

 

 关于缓存

Eureka Server有两个缓存,一个只读缓存,一个是读写缓存。

  1. 读写缓存:每次有服务实例状态变化(如注册服务、下线等)只会更新读写缓存
  2. 只读缓存永不过期,但是可能存在过期数据。此时为了保证只读数据的准确性,会有个定时器定时同步两个缓存,然后将状态变化的服务实例添加到”最近租约变更记录队列”。执行频率默认30s

所以 ,为了提高实时性,可以考虑关闭只读缓存。

"最近租约变更记录队列" 里的数据也有有效期,默认为180s。

  1. 当有注册、状态变更、下线时,会将创建最近租约变更记录加入此队列中
  2. 用于注册信息增量获取
  3. 后台任务定时顺序扫描队列,当数据最后更新时间超过一定时长后进行移除

相关常用配置:

# 是否开启只读请求响应缓存。响应缓存 ( ResponseCache ) 机制目前使用两层缓存策略。优先读取只读缓存,读取不到后读取固定过期的读写缓存。
eureka.server.use-read-only-response-cache: true
# 只读缓存更新频率,单位:毫秒。只读缓存定时更新任务只更新读取过请求 ,因此虽然永不过期,也会存在读取不到的情况。
eureka.server.response-cache-update-interval-ms: 30s
#getResponseCacheAutoExpirationInSeconds() :读写缓存写入后过期时间,单位:秒。
eureka.server.response-cache-auto-expiration-in-seconds: 180s

# 移除队列里过期的租约变更记录的定时任务执行频率,单位:毫秒。默认值 :30 * 1000 毫秒。
eureka.server.delta-retention-timer-interval-in-ms: 30s
# 租约变更记录过期时长,单位:毫秒。默认值 : 3 * 60 * 1000 毫秒。
eureka.server.retention-time-in-m-s-in-delta-queue: 180s

服务端其他配置信息:

由于可选配置项较多,详情可以参考 :Eureka配置详解

 

Eureka Server – 自我保护机制

我们知道,当一个服务在定义的服务失效的时间内失去心跳时,服务端会将其从服务列表中移除.但有一种情况下它不会移除服务,当Eureka-Server启动自我保护时.

  • Renews threshold: Eureka Server 期望每分钟最少收到服务实例心跳的总数 
  • Renews threshold = 当前注册的服务实例数 x 2 * 自我保护系数( eureka.renewalPercentThreshold )

之所以 x 2,是因为Eureka 硬编码(默认)认为每个实例每 30 秒 1 个心跳,即1分钟2个心跳
Renews (last min): Eureka Server 最近 1 分钟收到服务实例心跳的总数

当启动自我保护时,如果Eureka Server最近1分钟收到服务实心跳的次数小于阈值(即预期的最小值),则会触发自我保护模式,此时Eureka Server此时会认为这是网络问题,它不会注销任何过期的实例。等到最近收到心跳的次数大于阈值后,则Eureka Server退出自我保护模式

相关配置:

# 配置在Eureka Server端的配置文件 
#是否开启自我保护模式。
eureka.server.enable-self-preservation: true

#开启自我保护模式比例,超过该比例后开启自我保护模式。
eureka.server.renewal-percent-threshold: 0.85

#自我保护模式比例更新定时任务执行频率。
eureka.server.renewal-threshold-update-interval-ms: 900s

默认阀值为85%,即当1分钟内失去85%的心跳时,开启自我保护模式

比如注册中心单独部署在一个集群中,所有服务向其注册服务完毕后,突然注册中心所在的集群网络和其它服务网络断开,但是服务实例之间是可达的,整个服务可以正常工作。此时如果没有开启自我保护机制,经过Ns后,注册中心清理所有的注册服务,然后网络恢复了,那么其它服务进行更新时,会认为其它服务已经掉线,则从本地移除所有的服务请求,则整个服务变的不可用 
关于自我保护阈值,需要设置合理值。比如我们将全部服务均匀地部署在2台服务器,则需要设置阈值小于50%,否则如果出来一台服务器坏掉了,则此时就要求注册中心移除一般的服务注册中心是合理的,不应该启动自我保护.

 

执行清理过期租约的逻辑

首先计算最大允许清理租约的数量,后计算允许清理租约的数量 
最大允许清理租约数量 =  (1-renewalPercentThreshold)*当前注册的应用实例数,若默认为0.85,则最大允许清理15%*注册实例数.
实际清理租约数量 = Math.min(所有过期注册信息个数,最大允许清理租约数量) 
若Math.min为最大允许清理租约数,则说明当前过期的注册信息数大于最大允许清理租约数,则会随机清理过期的租约 
如果同时过期大量注册信息,则需要好久才能将注册信息清理完毕.

是否开启自我保护的差别,在于是否执行清理过期租约逻辑。如果想关闭分批逐步过期,设置 renewalPercentThreshold = 0 。

 

3.Eureka集群

为了提高高可用,Eureka是提供集群功能。不仅Eureka Client端可以集群,做为注册中心的Eureka Server也可以集群 。

多个Eureka Client进程向注册中心注册的Eureka Service Id相同,则被认为是一个集群。 
多个Eureka Server相互注册,组成一个集群

每个Eureka Server注册的消息发生变化时,会各个服务之间定时同步,中间过程每个服务的数据可能不一致,但是最终会变得一致 . Eureka Server集群之间的状态是采用异步方式同步的,所以不保证节点间的状态一定是一致的,不过基本能保证最终状态是一致的。
 

 

集群重要的类com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl

为了保证集群里所有Eureka Server节点的状态同步,所有以下操作都会同步到集群的所有服务上:服务注册(Registers)、服务更新(Renewals)、服务取消(Cancels),服务超时(Expirations)和服务状态变更(Status Changes)。

Server端发生的操作:

  • syncUp:在Eureka Server重启或新的Eureka Server节点加进来的,会执行初始化,从而能够正常提供服务。当Eureka server启动时,他会从其它节点获取所有的注册信息。如果获取同步失败,它在一定时间(此值由决定)内拒绝服务。
  • replicateToPeers: 复制所有的eureka操作到集群中其他节点,请求再次转发到其它的Eureka Server,调用同样的接口,传入同样的参数,除了会在header中标记isReplication=true,从而避免重复的replicate
  • register: 注册登录的实例,并且复印此实例的信息到所有的eureka server的节点。如果其它Eureka server调用此节点,只在本节点更新实例信息,避免通知其他节点执行更新

当有新的节点加入到集群中,会对现在Eureka Server和Eureka Client有什么影响以及他们如何发现新增的Eureka Server节点:

  • 新增的Eureka Server:在Eureka Server重启或新的Eureka Server节点加进来的,它会从集群里其它节点获取所有的实例注册信息。如果获取同步失败,它会在一定时间(此值由决定决定)内拒绝服务。
  • 已有Eureka Server如何发现新的Eureka Server: 
  1. 已有的Eureka Server:在运行过程中,Eureka Server之间会定时同步实例的注册信息。这样即使新的Application Service只向集群中一台注册服务,则经过一段时间集群中所有的Eureka Server都会有这个实例的信息。那么Eureka Server节点之间如何相互发现,各个节点之间定时(时间由eureka.server.peer-eureka-nodes-update-interval-ms决定)更新节点信息,进行相互发现。
  2. Service Consumer:Service Consumer刚启动时,它会从配置文件读取Eureka Server的地址信息。当集群中新增一个Eureka Server中时,那么Service Consumer如何发现这个Eureka Server?Service Consumer会定时(此值由eureka.client.eureka-service-url-poll-interval-seconds决定)调用Eureka Server集群接口,获取所有的Eureka Server信息的并更新本地配置。

相关配置项如下:

== Eureka-Server 集群同步相关
# Eureka-Server 启动时,从远程 Eureka-Server 读取不到注册信息时,多长时间不允许 Eureka-Client 访问。
eureka.server.wait-time-in-ms-when-sync-empty:5分钟

#Eureka-Server 集群节点更新频率,单位:毫秒。
eureka.server.peer-eureka-nodes-update-interval-ms:

# 初始化实例信息到Eureka服务端的间隔时间,单位为秒
eureka.client.initial-instance-info-replication-interval-seconds: 40

# 更新实例信息的变化到Eureka服务端的间隔时间,单位为秒
eureka.client.instance-info-replication-interval-seconds: 30

 近期微服务的学习很充实,尤其是当研究到其底层原理时,我觉得对我们的成长帮助是非常大的,只有打好的地,后面的学习才会不那么吃力.

以上总结参考自:

https://blog.csdn.net/hry2015/article/details/82597311

https://www.cnblogs.com/li3807/p/7282492.html

http://www.cnblogs.com/fangfuhai/p/7070325.html

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值