转载

从0开始用SpringCloud搭建微服务系统【二】

  • Eureka 是 Netflix 开源的一个 Restful 服务,主要用于服务的注册发现。它由两个组件组成:Eureka 服务器 和Eureka 客户端。
  • Eureka 服务器用作服务注册服务器。各个节点启动后,会在 Eureka Server 中进行注册,这样 Eureka Server 中的服务注册表中将会存储所有可用服务节点的信息。
  • Eureka 客户端是一个 Java 客户端,用来简化与服务器的交互,作为轮询负载均衡器,发现相关的服务,并提供服务的故障切换。
  • Eureka 在设计时就优先保证可用性。 即在 CAP 理论中,Eureka 满足 AP。Eureka 各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。
  • Eureka 的几个时间点:
    • 在应用启动后,将会向 Eureka Server 发送心跳,默认周期为 30 秒。
    • 如果 Eureka Server 在多个心跳周期内没有接收到某个节点的心跳,Eureka Server 将会从服务注册表中把这个服务节点移除(默认90秒)。
    • Eureka Client 对已经获取到的注册信息做了 30s 缓存。即服务通过 Eureka 客户端第一次查询到可用服务地址后会将结果缓存,下次再调用时就不会真正向 Eureka 发起 HTTP 请求了。
    • 负载均衡组件 Ribbon 也有 30s 缓存。Ribbon 会从上面提到的 Eureka Client 获取服务列表,然后将结果缓存 30s。
    • 最大可能出现2分钟的延迟 :注册延迟30s + Eureka服务器响应延迟30s + Eureka客户端更新延迟30s + Ribbon服务列表更新延迟30s。
  • Eureka 启动保护机制:

    • 如果在 15 分钟内超过 85% 的节点都没有正常的心跳,那 么Eureka 就认为客户端与注册中心出现了网络故障。

    • 自我保护机制的触发条件:

      条件:当每分钟心跳次数( renewsLastMin ) 小于 numberOfRenewsPerMinThreshold 时,并且开启自动保护模式开关( eureka.server.enable-self-preservation = true ) 时,触发自我保护机制,不再自动过期续约。

      其中:

      1. numberOfRenewsPerMinThreshold = expectedNumberOfRenewsPerMin * 续租百分比( eureka.server.renewalPercentThreshold, 默认0.85 )

      2. expectedNumberOfRenewsPerMin = 当前注册的应用实例数 x 2 。为什么乘以 2: 默认情况下,注册的应用实例每半分钟续租一次,那么一分钟心跳两次,因此 x 2 。

        解释:服务实例数10个,期望每分钟续约数10 2=20,期望阈值20 0.85=17,自我保护少于17时 触发。】

    • Eureka 启动保护机制会出现以下情况:

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

架构原理图

从0开始用SpringCloud搭建微服务系统【二】

Eureka Server

引入依赖 spring-cloud-starter-netflix-eureka-server , 在启动类 Application 上,添加 @EnableEurekaServer 注解。

如果是单机服务,可以在 application.yml 中使用以下配置:

eureka:
environment: dev  # 设置环境,可选
server:
enable-self-preservation: false  # 中小规模下,自我保护模式坑比好处多,所以关闭它
renewal-threshold-update-interval-ms: 120000  # 心跳阈值计算周期,如果开启自我保护模式,可以改一下这个配置
eviction-interval-timer-in-ms: 5000 # 主动失效检测间隔,配置成5秒
use-read-only-response-cache: false # 禁用readOnlyCacheMap
wait-time-in-ms-when-sync-empty: 0  #在Eureka服务器获取不到集群里对等服务器上的实例时,需要等待的时间,单机模式设置为0
client:
healthcheck: true
service-url:
defaultZone: http://${webfuse-security.user.name}:${webfuse-security.user.password}@localhost:2000/eureka/
registry-fetch-interval-seconds: 5  # 定时刷新本地缓存时间
register-with-eureka: false #表示是否将自己注册到Eureka Server,默认为true。由于当前这个应用就是Eureka Server,故而设为false。
fetch-registry: false #表示是否从Eureka Server获取注册信息,默认为true。因为这是一个单点的Eureka Server,不需要同步其他的Eureka Server节点的数据,故而设为false。
instance:
hostname: ${hostname:localhost}
instance-id: ${spring.application.name}@${spring.cloud.client.ip-address}:${server.port}    # 自定义实例ID
prefer-ip-address: true
lease-expiration-duration-in-seconds: 10  # 没有心跳的淘汰时间,10秒
lease-renewal-interval-in-seconds: 5  # 心跳间隔,5秒

启动服务,可在 http://localhost:2000 查看项目页面。

Eureka Client

EurekaClient 可以在客户端获取eureka服务器上的注册者信息。

引入依赖 spring-cloud-starter-netflix-eureka-client , 在启动类 Application 上,添加 @EnableDiscoveryClient 注解。

application.yml 中使用以下配置:

eureka:
environment: dev
client:
healthcheck:
enabled: true
service-url:
defaultZone: http://user:[email protected]:8010/eureka/
registry-fetch-interval-seconds: 5  # 定时刷新本地缓存时间
instance:
hostname: ${hostname:localhost}
instance-id: ${spring.application.name}@${spring.cloud.client.ip-address}:${server.port}    # 自定义实例ID
prefer-ip-address: true
lease-expiration-duration-in-seconds: 10  # 没有心跳的淘汰时间,10秒
lease-renewal-interval-in-seconds: 5  # 心跳间隔,5秒

启动项目即可。可在 http://localhost:2000 中看到注册的状态。

Eureka 高可用

Eureka Server 之间是可以互相注册的。

举个例子,我们有 3 个 Eureka 注册中心,端口分别为 2001 、 2002 和 2003 。那么端口为 2001 的最基本的配置如下:

eureka:
  client:
    service-url:
      defaultZone: http://localhost:2002/eureka/,http://localhost:2003/eureka/

端口 2002 和 2003 服务可以根据以上规则配置。

多网卡选择

服务器有多个网卡,eh0,eh1,eh2,只有eh0可以让外部其他服务访问进来,而Eureka client将eh1和eh2注册到Eureka server上,这样其他服务就无法访问该微服务了。有两种方式:

  • 指定IP注册

    eureka:
    instance:
    prefer-ip-address: true
    ip-address: 实际能访问到的IP
    
  • 使用 spring.cloud.inetutils 配置网卡选择

Eureka健康检查

由于server和client通过心跳保持 服务状态,而只有状态为UP的服务才能被访问。看eureka界面中的status。

比如心跳一直正常,服务一直UP,但是此服务DB连不上了,无法正常提供服务。

此时,我们需要将 微服务的健康状态也同步到server。只需要启动eureka的健康检查就行。这样微服务就会将自己的健康状态同步到eureka。配置如下即可。

在client端加入Actuator,并配置 eureka.client.healthcheck.enabled=true ,将自己真正的健康状态传播到server。

通过代码来改动服务的状态:

@Component
@Data
public class HealthStatusHandlerimplements HealthIndicator{
    private Boolean status = true;
    @Override
    public Health health(){
        if (status) {
            return new Health.Builder().up().build();
        }
        return new Health.Builder().down().build();
    }
}

应用场景:比如说短信业务,欠费了等情况,可以暂时下线服务。

Eureka遇到的坑

available-replicas为空的问题

如果使用了 eureka.instance.prefer-ip-address: true ,然后 eureka.client.service-url.defaultZone 配置的IP与实例IP不一致,会出现available-replicas为空的问题。

解决方法:在 eureka.instance.ip-address 中强制设置IP,然后在 eureka.client.service-url.defaultZone 配置对应的IP。

Eureka 最佳实践

本人所在的公司由于体量小, 生产环境直接使用 Eureka 的默认配置进行高可用性运行 ,目前也没有出现太大的问题。

以下是一些实践参考文章(注意:文章中的版本号不是最新的,可能在配置上会有调整):

  • Spring Cloud中,Eureka常见问题总结
  • Eureka Clustering documentation and best practices
  • Documentation: changing Eureka renewal frequency WILL break the self-preservation feature of the server
原文  http://webfuse.cn/2020/04/22/从0开始用SpringCloud搭建微服务系统【二】/
正文到此结束
Loading...