CAP定理



Zookeeper


ZK 的设计原则是 CP,即强一致性和分区容错性。它保证数据的强一致性,但舍弃了可用性。如果出现网络问题可能会影响 ZK 的选举,导致 ZK 注册中心的不可用。


Eureka & Nacos


Eureka 的设计原则是 AP,即可用性和分区容错性。它保证了注册中心的可用性,但舍弃了数据一致性,各节点上的数据有可能是会不一致的(会最终一致)。


差异


ZK 是注册中心跟服务之间建立长链接来建立联系的,服务提供者挂了注册中心会实时感知到,实时推送最新的注册信息给服务消费者。


Eureka 和 Nacos 这些 Spring Cloud 系的注册中心都是用定时任务 HTTP 短连接轮训去定时汇报、拉取路由信息的,服务提供者挂了,服务消费者会持有一段时间的脏数据,直到定时任务下一次去注册中心拉取最新的路由信息。


Eureka 服务调用关系


服务提供者


  1. 服务启动后向注册中心发起 register 请求,注册服务。
  2. 在运行过程中定时向注册中心发送 renew 心跳,证明 "我还活着"。
  3. 非暴力停止服务时向注册中心发起 cancel 请求,清空当前服务注册信息。


服务消费者


  1. 服务启动后从注册中心拉取服务注册信息。
  2. 在运行过程中开启定时任务定期去注册中心拉取最新的服务路由信息。
  3. 服务消费者发起远程调用:


服务不可用


数据不一致导致远程调用请求到了已被停止的服务。


即使旧服务进程被正常停止,向注册中心发送了 cancel 请求,让注册中心清空了自己的注册信息。其他服务消费者还是会持有旧的路由信息,直到下一次定时任务执行去注册中心拉取最新的路由信息,不采取措施的话这期间产生的远程调用还是会打到已停止的服务去,疯狂报 500。


降低不可用时长


调小轮训拉取注册信息的时间


无论如何我们都不可能达到一致性(除非放弃可用性),只能尽可能的降低不可用时长。


这些值不能太大也不能太小, 如果该值太大,则很可能将流量转发过去的时候,该 instance 已经不存活了。如果该值设置太小了,则 instance 则很可能因为临时的网络抖动而被摘除掉。


下面只保留了我们需要关注的配置,我这边注册中心与服务都处于专有网络,所以间隔时间较短,需要根据实际场景调整参数值。


Eureka


EurekaClientConfigBean


eureka:
  client:
    instance-info-replication-interval-seconds: 5   # 每间隔5秒向注册中心更新自己的状态 (秒,30)
    registry-fetch-interval-seconds: 5              # 每间隔5秒到注册中心获取一次路由信息 (秒,30)
  instance:
    lease-renewal-interval-in-seconds: 5            # 该服务实例向注册中心发送心跳间隔 (秒,30)
    lease-expiration-duration-in-seconds: 9         # 实例删除的超时时间,即服务端9秒收不到客户端心跳,会将客户端注册的实例删除 (秒,90)


Nacos


NacosDiscoveryProperties


spring:
  cloud:
    nacos:
      discovery:
        watch—delay: 5000           # 每间隔5秒到注册中心获取一次注册信息 (毫秒, 30000)
        metadata:
          heart-beat-interval: 3    # 心跳包发送周期,单位为秒
          heart-beat-timeout: 6     # 心跳超时时间,即服务端6秒收不到心跳,会将客户端注册的实例设为不健康
          ip-delete-timeout: 9      # 实例删除的超时时间,即服务端9秒收不到客户端心跳,会将客户端注册的实例删除


调整 Ribbon 重试机制


# 客户端负载均衡配置(不要试图改变key样式 ReadTimeout => read-timeout)
ribbon:
  eureka:
    enabled: true
  ReadTimeout: 5000                         # 接口处理超时时间
  ConnectTimeout: 5000                      # 连接超时时间
  MaxAutoRetries: 0                         # 同实例最大自动重试次数
  MaxAutoRetriesNextServer: 1               # 换实例重试次数
  MaxTotalHttpConnections: 2000             # 最大http连接数 越大越好 但到到达一个临界点之后 就不会提高响应速度了
  MaxConnectionsPerHost: 1000               # 每个host连接数

# See https://github.com/Netflix/Hystrix/wiki/Configuration
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            # https://stackoverflow.com/questions/50622668/hystrix-ribbon-timeout-warnings
            timeoutInMilliseconds: 20000    # 断路器的超时时间需要大于Ribbon的超时时间,不然不会触发重试。


服务分批升级


先发布一批做验证,发现问题立即回滚,没问题再发布下一批,直到全部节点发布完毕。


参考资料