Eureka

发布时间 2023-12-15 10:44:58作者: 安浩阳

Eureka

Eureka是什么?

Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要用于定位运行在AWS域中的中间层服务,以达到负载均衡和中间层服务故障转移的目的。

SpringCloud将它集成在其子项目spring-cloud-netflix中,以实现SpringCloud的服务发现功能

偷张图更直观地了解一下:

image

如上图所示,服务提供方会将自己注册到EurekaServer中,这样EurekaServer就会存储各种服务信息,而服务消费方想要调用服务提供方的服务时,直接找EurekaServer拉取服务列表,然后根据特定地算法(轮询、随机......),选择一个服务从而进行远程调用

  • 服务提供方:一次业务中,被其它微服务调用的服务。(提供接口给其它微服务)
  • 服务消费方:一次业务中,调用其它微服务的服务。(调用其它微服务提供的接口)

服务提供者与服务消费者的角色并不是绝对的,而是相对于业务而言

如果服务A调用了服务B,而服务B又调用了服务C,服务B的角色是什么?

  • 对于A调用B的业务而言:A是服务消费者,B是服务提供者
  • 对于B调用C的业务而言:B是服务消费者,C是服务提供者

因此,服务B既可以是服务提供者,也可以是服务消费者

Eureka的自我保护机制

image

这张图中EurekaServer和服务提供方有一个心跳检测机制,这是EurekaServer为了确定这些服务是否还在正常工作,所以进行的心跳检测

eureka-client启动时, 会开启一个心跳任务,向Eureka Server发送心跳,默认周期为30秒/次,如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)

eureka-server维护了每个实例的最后一次心跳时间,客户端发送心跳包过来后,会更新这个心跳时间

eureka-server启动时,开启了一个定时任务,该任务每60s/次,检查每个实例的最后一次心跳时间是否超过90s,如果超过则认为过期,需要剔除

但是EurekaClient也会因为网络等原因导致没有及时向EurekaServer发送心跳,因此EurekaServer为了保证误删服务就会有一个“自我保护机制”,俗称“好死不如赖活着”

如果在短时间内EurekaServer丢失过多客户端时 (可能断网了,低于85%的客户端节点都没有正常的心跳 ),那么Eureka Server就认为客户端与注册中心出现了网络故障,Eureka Server自动进入自我保护状态 。Eureka的这样设计更加精准地控制是网络通信延迟,而不是服务挂掉了,一旦进入自我保护模式,那么 EurekaServer就会保留这个节点的属性,不会删除,直到这个节点恢复正常心跳

  • 85% 这个阈值,可以通过如下配置来设置:
eureka:
  server:
    renewal-percent-threshold: 0.85

这里存在一个问题,这个85%是超过谁呢?这里有一个预期的续约数量,计算公式如下:

自我保护阀值 = 服务总数 * 每分钟续约数(60S/客户端续约间隔) * 自我保护续约百分比阀值因子

在自我保护模式中,EurekaServer会保留注册表中的信息,不再注销任何服务信息,当它收到正常心跳时,才会退出自我保护模式,也就是:宁可保留错误的服务注册信息,也不会盲目注销任何可能健康的服务实例,即:好死不如赖活着

因此Eureka进入自我保护状态后,会出现以下几种情况:

  • Eureka Server仍然能够接受新服务的注册和查询请求,但是不会被同步到其他节点上,保证当前节点依然可用。Eureka的自我保护机制可以通过如下的方式开启或关闭
eureka:
  server:
#   开启Eureka自我保护机制,默认为true
    enable-self-preservation: true
  • Eureka Server不再从注册列表中移除因为长时间没有收到心跳而应该剔除的过期服务,如果在保护期内这个服务提供者刚好非正常下线了,此时服务消费者就会拿到一个无效的服务实例,此时会调用失败,对于这个问题需要服务消费者端要有一些容错机制,如重试,断路器等!

Eureka常用配置

eureka:
  client: # eureka客户端配置
    register-with-eureka: true # 是否将自己注册到eureka服务端上去
    fetch-registry: true # 是否获取eureka服务端上注册的服务列表
    service-url:
      defaultZone: http://localhost:8001/eureka/ # 指定注册中心地址。若是集群可以写多个,中间用 逗号 隔开
    enabled: true # 启用eureka客户端
    registry-fetch-interval-seconds: 30 # 定义去eureka服务端获取服务列表的时间间隔
  instance: # eureka客户端实例配置
    lease-renewal-interval-in-seconds: 30 # 定义服务多久去注册中心续约
    lease-expiration-duration-in-seconds: 90 # 定义服务多久不去续约认为服务失效
    metadata-map:
      zone: hangzhou # 所在区域
    hostname: localhost # 服务主机名称
    prefer-ip-address: false # 是否优先使用ip来作为主机名
  server: # eureka服务端配置
    enable-self-preservation: false #关 闭eureka服务端的自我保护机制

使用Eureka

实现如下的逻辑:

image

搭建Eureka Server

自行单独创建一个Maven项目,导入依赖如下:

<!--Eureka Server-->
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

在YAML文件中一般可配置内容如下:

server:
  port: 21006
spring:
  application:
    name: EUREKA-SERVER
eureka:
  instance:
    # Eureka的主机名,是为了eureka集群服务器之间好区分
    hostname: 127.0.0.1
    # 最后一次心跳后,间隔多久认定微服务不可用,默认90
    lease-expiration-duration-in-seconds: 90
  client:
    # 不向注册中心注册自己。应用为单个注册中心设置为false,代表不向注册中心注册自己,默认true	注册中心不需要开启
    registerWithEureka: false
    # 不从注册中心拉取自身注册信息。单个注册中心则不拉取自身信息,默认true	注册中心不需要开启
    fetchRegistry: false
    service-url:
      # Eureka Server的地址,集群就写多个,用逗号分隔
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka
#    server:
#      # 开启Eureka自我保护机制,默认为true
#      enable-self-preservation: true

启动类编写内容如下:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

/**
 * <p>@description  : 该类功能  eureka server启动类
 * </p>
 * <p>@author       : ZiXieqing</p>
 */

/*@EnableEurekaServer 开启Eureka Server功能*/
@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class, args);
    }
}

搭建客户端

引入依赖

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

配置yaml

server:
  port: 8080
spring:
  application:
    name: ORDER-SERVICE
eureka:
  client:
    service-url:
      # 向哪个eureka server进行服务拉取
      defaultZone: http://localhost:10086/eureka