Eureka

  • 1.Eureka是什么
  • 2.Eureka基本架构和简单原理
  • Eureka Server
  • EurekaClient
  • 3.Eureka和Zookeeper的区别
  • 3.Eureka三大角色和流程
  • 4.单个Eureka Server配置部署实例
  • 5.Eureka Server注册中心(高可用)集群部署
  • demo
  • 6.Eureka Client配置部署实现
  • 7.Eureka 的注解
  • 8.Eureka 的配置文件
  • 9.Eureka 的自我保护模式
  • 10.zone


1.Eureka是什么

Eureka是Netflix的一个子模块,也是核心模块之一。是一个基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移。

服务注册与发现对于微服务架构来说是非常重要的,有了服务发现与注册,只需要使用服务的标识符,就可以访问到服务,而不需要修改服务调用的配置文件了。功能类似于dubbo的注册中心,比如Zookeeper。


2.Eureka基本架构和简单原理

Eureka 采用了 C-S 的设计架构。Eureka Server 作为服务注册功能的服务器,它是服务注册中心。

而系统中的其他微服务,使用 Eureka 的客户端连接到 Eureka Server并维持心跳连接。这样系统的维护人员就可以通过 Eureka Server 来监控系统中各个微服务是否正常运行。SpringCloud 的一些其他模块(比如Zuul)就可以通过 Eureka Server 来发现系统中的其他微服务,并执行相关的逻辑。

Eureka包含两个组件:Eureka Server和Eureka Client

Eureka Server

Eureka Server提供服务注册服务(通常就是微服务中的注册中心)

各个节点启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务节点的信息可以在界面中直观的看到

EurekaClient

EurekaClient是一个Java客户端,用于简化Eureka Server的交互。 (通常就是微服务中的客户端和服务端)

客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务节点移除(默认90秒)


3.Eureka和Zookeeper的区别

根据CAP理论,一个分布式系统无法同时满足C(一致性)、A(可用性)和P(分区容错性)。由于分区容错性P在是分布式系统中必须要保证的,因此我们只能在A和C之间进行权衡。
在此Zookeeper保证的是CP, 而Eureka则是AP。

Zookeeper保证CP

当向注册中心查询服务列表时,我们可以容忍注册中心返回的是几分钟以前的注册信息,但不能接受服务直接down掉不可用。
也就是说,服务注册功能对可用性的要求要高于一致性。但是zk会出现这样一种情况,当master节点因为网络故障与其他节点失去联系时,剩余节点会重新进行leader选举。
问题在于,选举leader的时间太长,30~120s,且选举期间整个zk集群都是不可用的,这就导致在选举期间注册服务瘫痪。
在云部署的环境下,因网络问题使得zk集群失去master节点是较大概率会发生的事,虽然服务能够最终恢复,但是漫长的选举时间导致的注册长期不可用是不能容忍的。

Eureka保证AP

Eureka看明白了这一点,因此在设计时就优先保证可用性。
Eureka各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。
而Eureka的客户端在向某个Eureka注册或时如果发现连接失败,则会自动切换至其它节点,只要有一台Eureka还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)。

除此之外,Eureka还有一种自我保护机制,如果在15分钟内超过85%的节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,此时会出现以下几种情况:

  1. Eureka不再从注册列表中移除因为长时间没收到心跳而应该过期的服务
  2. Eureka仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用)
  3. 当网络稳定时,当前实例新的注册信息会被同步到其它节点中

因此, Eureka可以很好的应对因网络故障导致部分节点失去联系的情况,而不会像zookeeper那样使整个注册服务瘫痪。


3.Eureka三大角色和流程

eureka有三个重要角色

  • Eureka Server 服务注册与发现中心 提供服务注册和发现
  • Service Provider服务提供方 将自身服务注册到Eureka,从而使服务消费方能够找到
  • Service Consumer服务消费(调用)方 从Eureka获取注册服务列表,从而能够消费服务

springcloud iframe 统一身份认证和保持登录状态的问题_Server


springcloud iframe 统一身份认证和保持登录状态的问题_Server_02

由上可知,各个微服务启动时,会通过 Eureka Client 向 Eureka Server 注册自己,Eureka Server 会存储该服务的信息,这就衍生出了微服务相互识别的话题。

注册: Eureka Client 向 Eureka Server 注册自己,Eureka Server 会存储该服务的信息 (registry)

同步:每个 Eureka Server 同时也是 Eureka Client(逻辑上的)。多个 Eureka Server 之间通过复制的方式完成服务注册表的同步,形成 Eureka 的高可用 (replicate)
   
识别:Eureka Client 会缓存 Eureka Server 中的信息。即使所有 Eureka Server 节点都宕掉,服务消费者仍可使用缓存中的信息找到服务提供者 (remote call)
   
续约:微服务会周期性(默认30s)地向 Eureka Server 发送心跳以Renew(续约)信息(类似于heartbeat)

续期:Eureka Server 会定期(默认60s)执行一次失效服务检测功能,它会检查超过一定时间(默认90s)没有Renew的微服务,发现则会注销该微服务节点 (cancel)
   
Spring Cloud 已经把 Eureka 集成在其子项目 Spring Cloud Netflix 里面


4.单个Eureka Server配置部署实例

pom.xml

<dependencies>
	   <!--eureka-server服务端 -->
	   <dependency>
	     <groupId>org.springframework.cloud</groupId>
	     <artifactId>spring-cloud-starter-eureka-server</artifactId>
	   </dependency>
	   <dependency>
	     <groupId>org.springframework.boot</groupId>
	     <artifactId>spring-boot-starter</artifactId>
	   </dependency>
	   <!-- 修改后立即生效,热部署 -->
	   <dependency>
	     <groupId>org.springframework</groupId>
	     <artifactId>springloaded</artifactId>
	   </dependency>
	   <dependency>
	     <groupId>org.springframework.boot</groupId>
	     <artifactId>spring-boot-devtools</artifactId>
	   </dependency>
  </dependencies>

注册中心 主启动类

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
 
@SpringBootApplication
@EnableEurekaServer//EurekaServer服务器端启动类,接受其它微服务注册进来
public class EurekaServer7001_App
{
  public static void main(String[] args)
  {
   SpringApplication.run(EurekaServer7001_App.class, args);
  }

配置文件

## server
server.port=8081

##eureka

#指定环境
eureka.environment=work

#eureka服务端的实例名称
eureka.instance。hostname: localhost 

# 设置是否将自己作为客户端注册到注册中心(缺省true)
# 这里为不需要,false表示不向注册中心注册自己。(查看@EnableEurekaServer注解的源码,会发现它间接用到了@EnableDiscoveryClient)

eureka.client.register-with-eureka=false

# 设置是否从注册中心获取注册信息(缺省true)
# false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务。因为这是一个单点的EurekaServer,不需要同步其它EurekaServer节点的数据,故设为false
eureka.client.fetch-registry=false

#设置与Eureka Server交互的地址。查询服务和注册服务都需要依赖这个地址。
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${server.port}/eureka/ 

#是否开启自我保护模式,默认为true。
eureka.server.enable-self-preservation=true

#续期时间,即扫描失效服务的间隔时间(缺省为60*1000ms)
eureka.server.eviction-interval-timer-in-ms=10000

5.Eureka Server注册中心(高可用)集群部署

Eureka Server 支持运行多实例,并以互相注册的方式(即伙伴机制),来实现高可用的部署。通过运行多个实例并要求它们相互注册,可以使Eureka更具弹性并可以使用。

即每一台 Eureka 都在配置中指定另一个 Eureka 或多个 地址作为伙伴,它在启动时会向伙伴节点获取注册列表

demo

与单Server配置稍作改动,如下三个注册中心ABC

  • A:127.0.0.1:8001
  • B:127.0.0.1:8002
  • C:127.0.0.1:8003
  • A配置: eureka.client.serviceUrl.defaultZone : http://B/eureka/,http://C/eureka/
  • B配置: eureka.client.serviceUrl.defaultZone : http://A/eureka/,http://C/eureka/
  • C配置: eureka.client.serviceUrl.defaultZone : http://B/eureka/,http://A/eureka/

运行不同环境

java -jar .\trade-0.0.1-SNAPSHOT.jar --spring.profiles.active=local


6.Eureka Client配置部署实现

pom.xml

<!-- 将微服务provider侧注册进eureka -->
   <dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-eureka</artifactId>
   </dependency>
   <dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-config</artifactId>
   </dependency>
      <!-- actuator监控信息完善 -->
   <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-actuator</artifactId>
   </dependency>

微服务client的启动类

@SpringBootApplication
@EnableEurekaClient //本服务启动后会自动注册进eureka服务中
public class DeptProvider8001_App
{
  public static void main(String[] args)
  {
   SpringApplication.run(DeptProvider8001_App.class, args);
  }
}

微服务client的配置文件

##eureka
#客户端注册进eureka服务列表内,该配置可配置多个注册中心,用,隔开
eureka.client.serviceUrl.defaultZone=http://localhost:8081/eureka/

#instance-id在注册中心显示的微服务名,Eureka 首页显示的微服务名默认为:机器主机名:应用名称:应用端口。即${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${server.port}}
eureka.instance.instance-id=${spring.application.name}:${server.port}

# 设置微服务调用地址为IP优先(缺省为false)访问路径可以显示IP地址
eureka.instance.prefer-ip-address=true

# 心跳时间,即服务续约间隔时间(缺省为30s)
eureka.instance.lease-renewal-interval-in-seconds=30

# 发呆时间,即服务续约到期时间(缺省为90s)
eureka.instance.lease-expiration-duration-in-seconds=90

7.Eureka 的注解

@EnableEurekaServer标志自己是EurekaServer,允许微服务注册进来

@EnableDiscoveryClient让注册中心能够发现,扫描到该服务。服务启动后会自动注册进eureka服务中。(可以是eureka、consul、zookeeper三种注册中心)

@EnableEurekaClient让注册中心能够发现,扫描到该服务。服务启动后会自动注册进eureka服务中。(只适用于Eureka作为注册中心)

EnableDiscoverClient和EnalbleEurekaClient的不同可见博客


8.Eureka 的配置文件

服务端的yaml文件

server:
  # 项目端口号
  port: 8761
eureka:
  instance:
    # 主机名
    hostname: localhost
    # 使用 ip 注册到注册中心实例化
    prefer-ip-address: true
    # 安全端口
    secure-port: ${server.port}
    # 指示是否应为流量启用安全端口
    secure-port-enabled: true
    # 关闭非安全端口
    non-secure-port-enabled: false
    home-page-url: https://${eureka.instance.hostname}:${server.port}/
    status-page-url: https://${eureka.instance.hostname}:${server.port}/actuator/info
    health-check-url: https://${eureka.instance.hostname}:${server.port}/actuator/health
  client:
    # 此实例是否从注册中心获取注册信息
    fetch-registry: false
    # 是否将此实例注册到注册中心
    register-with-eureka: false
    # 注册地址
    service-url:
      # 默认注册分区地址
      defaultZone: https://${eureka.instance.hostname}:${server.port}/eureka/
  server:
    # 同步为空时,等待时间
    wait-time-in-ms-when-sync-empty: 0
    # 是否开启自我保护机制
    ## 在分布式系统设计里头,通常需要对应用实例的存活进行健康检查,这里比较关键的问题就是要处理好网络偶尔抖动或短暂不可用时造成的误判。另外Eureka Server端与Client端之间如果出现网络分区问题,在极端情况下可能会使得Eureka Server清空部分服务的实例列表,这个将严重影响到Eureka server的 availibility属性。因此Eureka server引入了SELF PRESERVATION机制。
    ## Eureka client端与Server端之间有个租约,Client要定时发送心跳来维持这个租约,表示自己还存活着。 Eureka通过当前注册的实例数,去计算每分钟应该从应用实例接收到的心跳数,如果最近一分钟接收到的续约的次数小于指定阈值的话,则关闭租约失效剔除,禁止定时任务剔除失效的实例,从而保护注册信息。
    # 此处关闭可以防止问题(测试环境可以设置为false):Eureka server由于开启并引入了SELF PRESERVATION模式,导致registry的信息不会因为过期而被剔除掉,直到退出SELF PRESERVATION模式才能剔除。
    enable-self-preservation: false
    # 指定 Eviction Task 定时任务的调度频率,用于剔除过期的实例,此处未使用默认频率,频率为:5/秒,默认为:60/秒
    # 有效防止的问题是:应用实例异常挂掉,没能在挂掉之前告知Eureka server要下线掉该服务实例信息。这个就需要依赖Eureka server的EvictionTask去剔除。
    eviction-interval-timer-in-ms: 5000
    # 设置read Write CacheMap的expire After Write参数,指定写入多长时间后过期
    # 有效防止的问题是:应用实例下线时有告知Eureka server下线,但是由于Eureka server的REST API有response cache,因此需要等待缓存过期才能更新
    response-cache-auto-expiration-in-seconds: 60
    # 此处不开启缓存,上方配置开启一个即可
    # use-read-only-response-cache: false
    # 指定每分钟需要收到的续约次数的阈值,默认值就是:0.85
    renewal-percent-threshold: 0.85
    # 续约频率提高,默认:30
    leaseRenewalIntervalInseconds: 10

eureka配置文件的实践


9.Eureka 的自我保护模式

该模式主要用于在一组客户端和Eureka Server之间存在网络分区的情况下的保护措施。

默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒)。但是当网络分区故障发生时,微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险了

——因为微服务本身其实是健康的, 此时本不应该注销这个微服务。Eureka通过“自我保护模式”来解决这个问题

——当EurekaServer节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,EurekaServer就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该Eureka Server节点会自动退出自我保护模式。

在自我保护模式中,Eureka Server会保护服务注册表中的信息,不再注销任何服务实例。当它收到的心跳数重新恢复到阈值以上时,该Eureka Server节点就会自动退出自我保护模式。它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。一句话讲解:好死不如赖活着

综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留),也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。

一句话:某时刻某一个微服务不可用了,eureka不会立刻清理,依旧会对该微服务的信息进行保存

Eureka关于保护模式的文档


10.zone

上面的serviceUrl提到了defaultZone

Eureka提供了region和zone两个概念来进行分区

  • region:可以简单理解为地理上的分区,比如亚洲地区,或者华北地区,再或者北京等等,没有具体大小的限制。根据项目具体的情况,可以自行合理划分region。
  • zone:可以简单理解为region内的具体机房,比如说region划分为北京,然后北京有两个机房,就可以在此region之下划分出zone1,zone2两个zone。

springcloud iframe 统一身份认证和保持登录状态的问题_spring_03

Eureka Client 在启动时需要指定 Zone(机房),它会优先请求自己 Zone(机房) 的 Eureka Server 获取注册列表

同样的,Eureka Server 在启动时也需要指定 Zone,如果没有指定的话,其会默认使用 defaultZone

详见源码中的 getEurekaServerServiceUrls() 方法: