(二).SpringCloud 从入门到放弃-Eureka注册中心深入

文章目录

      • 深入了解Eureka
        • Eureka Server的REST API简介
          • REST API 列表
          • REST API 实例
        • Eureka的核心类
        • Eureka 的设计理念
          • 服务实例如何注册到注册中心
          • 服务实例如何从注册中心剔除
          • 分布式系统的CAP特性
          • Peer to Peer架构
        • Eureka参数调优及监控
          • 核心参数
          • 参数调优
      • 总结

深入了解Eureka

Eureka Server的REST API简介

前面介绍了一个最基本的Eureka Server 和Eureka Client,它是整个springcloud生态服务注册于发现的一个缩影
这里举的shi是使用java语言的client例子,但是Eureka Server提供了REST API,允许其他语言的应用通过HTTP REST的方式接入到Eureka的服务注册发现中

REST API 列表
操作 http动作 描述
注册新的应用实例 POST /eureka/apps/{appId} 可以输入json或者xml格式的body,成功返回204
注销应用实例 DELETE /eureka/apps/{appId}/{instanceId} 成功返回200
应用实例发送心跳 PUT /eureka/apps/{appId}/{instanceId} 成功返回200,如果instanceId不存在返回404
查询所有应用实例 GET /eureka/apps 成功返回200,输出json或者xml格式
查询指定appId应用实例 GET /eureka/apps/{appId}
根据指定appId和instanceId查询应用实例 GET /eureka/apps/{appId}/{instanceId}
根据指定instanceId查询应用实例 GET /eureka/instances/{instanceId}
暂停应用实例 PUT /eureka/apps/{appId}/{instanceId}/status?value=OUT_OF_SERVICE 成功返回200,失败返回500
恢复应用实例 DELETE/eureka/apps/{appId}/{instanceId}/status?value=UP(value参数可以不传) 成功返回200,失败返回500
更新元数据 PUT /eureka/apps/{appId}/{instanceId}/status?mametadata?key=value 成功返回200,失败返回500
根据vip地址查询 GET /eureka/vips/{vipAddress} 成功返回200,输出json或者xml格式
根据svip地址查询 GET /eureka/svips/{svipAddress} 成功返回200,输出json或者xml格式
REST API 实例
  • 查询所有应用实例 curl -i http://localhost:10086/eureka/apps
HTTP/1.1 200
Content-Type: application/xml
Transfer-Encoding: chunked
Date: Wed, 24 Apr 2019 07:23:38 GMT

<applications>
  <versions__delta>1versions__delta>
  <apps__hashcode>UP_1_apps__hashcode>
  <application>
    <name>SERVICE-ONEname>
    <instance>
      <instanceId>DESKTOP-2Q068VO:service-oneinstanceId>
      <hostName>DESKTOP-2Q068VOhostName>
      <app>SERVICE-ONEapp>
      <ipAddr>192.168.50.208ipAddr>
      <status>UPstatus>
      <overriddenstatus>UNKNOWNoverriddenstatus>
      <port enabled="true">8080port>
      <securePort enabled="false">443securePort>
      <countryId>1countryId>
      <dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
        <name>MyOwnname>
      dataCenterInfo>
      <leaseInfo>
        <renewalIntervalInSecs>30renewalIntervalInSecs>
        <durationInSecs>90durationInSecs>
        <registrationTimestamp>1556089288403registrationTimestamp>
        <lastRenewalTimestamp>1556090696963lastRenewalTimestamp>
        <evictionTimestamp>0evictionTimestamp>
        <serviceUpTimestamp>1556089288403serviceUpTimestamp>
      leaseInfo>
      <metadata>
        <management.port>8080management.port>
      metadata>
      <homePageUrl>http://DESKTOP-2Q068VO:8080/homePageUrl>
      <statusPageUrl>http://DESKTOP-2Q068VO:8080/actuator/infostatusPageUrl>
      <healthCheckUrl>http://DESKTOP-2Q068VO:8080/actuator/healthhealthCheckUrl>
      <vipAddress>service-onevipAddress>
      <secureVipAddress>service-onesecureVipAddress>
      <isCoordinatingDiscoveryServer>falseisCoordinatingDiscoveryServer>
      <lastUpdatedTimestamp>1556089288403lastUpdatedTimestamp>
      <lastDirtyTimestamp>1556089286739lastDirtyTimestamp>
      <actionType>ADDEDactionType>
    instance>
  application>
applications>
  • 根据appId查询 curl -i http://localhost:10086/eureka/apps/service-one
HTTP/1.1 200
Content-Type: application/xml
Transfer-Encoding: chunked
Date: Wed, 24 Apr 2019 07:25:32 GMT

<application>
  <name>SERVICE-ONEname>
  <instance>
    <instanceId>DESKTOP-2Q068VO:service-oneinstanceId>
    <hostName>DESKTOP-2Q068VOhostName>
    <app>SERVICE-ONEapp>
    <ipAddr>192.168.50.208ipAddr>
    <status>UPstatus>
    <overriddenstatus>UNKNOWNoverriddenstatus>
    <port enabled="true">8080port>
    <securePort enabled="false">443securePort>
    <countryId>1countryId>
    <dataCenterInfo class="com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo">
      <name>MyOwnname>
    dataCenterInfo>
    <leaseInfo>
      <renewalIntervalInSecs>30renewalIntervalInSecs>
      <durationInSecs>90durationInSecs>
      <registrationTimestamp>1556089288403registrationTimestamp>
      <lastRenewalTimestamp>1556090696963lastRenewalTimestamp>
      <evictionTimestamp>0evictionTimestamp>
      <serviceUpTimestamp>1556089288403serviceUpTimestamp>
    leaseInfo>
    <metadata>
      <management.port>8080management.port>
    metadata>
    <homePageUrl>http://DESKTOP-2Q068VO:8080/homePageUrl>
    <statusPageUrl>http://DESKTOP-2Q068VO:8080/actuator/infostatusPageUrl>
    <healthCheckUrl>http://DESKTOP-2Q068VO:8080/actuator/healthhealthCheckUrl>
    <vipAddress>service-onevipAddress>
    <secureVipAddress>service-onesecureVipAddress>
    <isCoordinatingDiscoveryServer>falseisCoordinatingDiscoveryServer>
    <lastUpdatedTimestamp>1556089288403lastUpdatedTimestamp>
    <lastDirtyTimestamp>1556089286739lastDirtyTimestamp>
    <actionType>ADDEDactionType>
  instance>
application>

其他的就不一一尝试了,大家可以自己创建eureka client 来测试相关的API,
需要注意的是 eureka client 必须是web工程

Eureka的核心类

作用
InstanceInfo 代表注册的服务实例
LeaseInfo 标识应用实例的租约信息
ServiceInstance 包含一些通用的方法,包括优雅的关闭Eureka Client
InstanceStatus 服务的状态

Eureka 的设计理念

服务实例如何注册到注册中心

本质上就是在服务启动的时候,需要调用Eureka Server的 REST API的register方法,去注册该应用实例的信息
对于java应用,可以使用Netflix的 Eureka Client封装的API去调用
对应springcloud应用,可以使用spring-cloud-starter-netflix-eureka-client,基于springboot的自动配置,帮你自动实现服务的注册

服务实例如何从注册中心剔除

正常情况在服务实例关闭应用时,应该通过钩子函数或者其他生命周期回调方法去调用Eureka Server 的REST API的de-register方法,来删除自身服务实例的信息
Eureka Server要求client定时续约,也就是发送心跳,如果超过一定时间没有进行续约操作,Server就会主动剔除,这一点Eureka Server使用的是心跳检测

分布式系统的CAP特性
  • Consistency : 数据一致性,可能由于网络故障等导致分布式系统中,数据多副本要保持数据不冲突
  • Availability : 在任何时候对集群进行操作,都能正常响应(可以再一定的延时内完成)
  • Partition Tolerance : 分区容忍性 ,发生通信故障的时候,整个集群被划分多个无法通信的分区时,集群仍然可用

对于分布式系统来说,由于网络的不可控,出现分区是不可避免的,所以分布式系统设计一般在AP或者CP中选择
Eureka是部署在AWS的背景下设计的,网络问题不可避免,所以选择AP
在实际生产中,服务注册中心保留可用及过期的数据总比丢失掉可用的数据要好,这样的话,集群的信息就不是强一致性的,这就需要客户端能够支持负载均衡和失败重试,在Netflix的生态中,由ribbon提供此功能

Peer to Peer架构

一般来说,分布式应用中多个副本的复制方式,可分为主从复制和对等复制

  • 主从复制

也就是最为常见的 master-slave模式
有一个主副本,其他都是从副本,对所有的写操作都提交到主副本,再由主副本同步到其他的从副本
写操作的压力都在主副本上,是整个系统的瓶颈,从副本可以分担读请求压力

  • 对等复制

副本之间不分主从,任何副本都可以进行读写操作,然后每个副本之间进行数据同步,不存在某一块陷入瓶颈
Eureka Server 采用的就是Peer to Peer模式
各个副本之间的数据同步和冲突解决是个大问题
peer节点之间的数据相互复制并不能一定成功,因此Eureka还通过实例于Server之间的心跳检测来进行数据的最终修复,即如果发现不一致,应用实例重新进行register操作

Eureka参数调优及监控

核心参数
  • client端基本参数
参数 默认值 说明
eureka.client.availability-zones 告知client有哪些region和availability-zones,支持配置修改运行时生效
eureka.client…filter.only-up-instances true 是否过滤出InstanceStatus为UP的实例
eureka.client.region us-east-1 指定该应用实例所在的region,AWS datacenters适用
eureka.client.register-with-eureka true 是否将该应用实例注册到eureka server
eureka.client.prefer-same-zone-eureka true 是否优先使用与该实例处于同一zone的Eureka Server(高可用注册中心)
eureka.instance.prefer-ip-address false 是否优先使用ip地址来代替host name作为实例 hostname
eureka.instance.lease-expiration-duration-in-seconds 90 指定 Eureka Client间隔多久向Eureka Server发送心跳来告知Client是否存活
eureka.instance.metadata-map 指定应用实例的元数据信息
eureka.client.on-demand-update-status-change true 是否将本地实例状态的更新通过ApplicationInfoManager实时同步到Eureka Server
  • Client端 定时任务参数
参数 默认值 说明
eureka.client.cache-refresh-executor-thread-pool-size 2 刷新缓存的CacheRefreshThread的线程池大小
eureka.client.cache-refresh-executor-exponential-back-off=bound 10 调度任务执行超时时下次的调度延时时间
eureka.client.heartbeat-executor-thread-pool-size 2 心跳线程HeartBeatThread线程池的大小
eureka.client.heartbeat-executor-thread-pool-size 10 调度任务执行超时下次调度延时时间
eureka.client.register-fetch-interval-seconds 30 CaCacheRefresh线程的调度频率
eureka.client.eureka-service-url-poll-interval-seconds 5*60 AsyncResolver.updateTask刷新Eureka Server地址的时间间隔
eureka.client.initial-instance-info-replication-interval-seconds 40 InstanceInfoReplication将实例信息变更同步到Eureka Server的初始延时时间
eureka.client.instance-info-replication-interval-seconds 30 InstanceInfoReplication将实例信息变更同步到Eureka Server的时间间隔
eureka.instance.lease-renewal-interval-in-seconds 30 Eureka Client向Eureka Server发送心跳的时间间隔
  • ** Client端 http参数**
参数 默认值 说明
eureka.client.eureka-server-connect-timeout 5 连接超时时间
eureka.client.eureka-server-read-timeout 8 读超时时间
eureka.client.eureka-server-total-connections 200 连接池最大活动数
eureka.client.eureka-server-total-connections-per-host 50 每个host能使用的最大连接数
eureka.client.eureka-connections-idle-timeout-seconds 30 连接池中连接的空闲时间
  • Server端基本参数
参数 默认值 说明
eureka.server.enable-self-preservation true 是否开启自我保护模式
eureka.server.renewal-percent-threshold-timeout 0.85 指定每分钟需要收到的续约次数的阈值
eureka.instance.registry.expected-number-of-renews-per-min 1 指定每分钟需要收到的续约次数,实际数值被写死为count*2,另外也会被更新
eureka.server.renewal-threshold-update-interval-ms 15分钟 指定updateRenewalThreshold定时任务的调度频率来动态更新
eureka.server.evicition-interval-timer-in-ms 60*1000 指定Eviction Task定时任务的频率,用于剔除过期的实例
  • Server端 Cache参数

Eureka Server为了提升REST API的性能,提供了两个缓存,一个是基于ConcurrentMap的readOnlyCacheMap,一个是基于Guava Cache的readWriteCacheMap

参数 默认值 说明
eureka.server.use-read-only-response-cache true 是否使用只读的response-cache
eureka.server.response-cache-update-interval-ms 30*1000 设置CacheUpdateTask的频率,只在response-cache设置为true才生效
eureka.server.response-cache-auto-expiration-in-seconds 180 设置readWriteCacheMap的expireAfterWrite参数,指定写入多久后过期
  • Server端 Peer参数
参数 默认值 说明
eureka.server.peer-eureka-nodes-update-interval-ms 10分钟 指定peerupdateTask的调度时间间隔,用于配置文件刷新peerEurekaNodes节点的配置信息
eureka.server.peer-eureka-status-refresh-time-interval-ms 30*1000 指定更新peer nodes状态更新的时间间隔
  • Server端 http参数

Eureka Server 需要和其他的peer节点进行通信,底层使用的是HTTPClient

参数 默认值 说明
eureka.server.peer-node-connect-timeout-ms 200 连接超时时间
eureka.server.peer-node-read-timeout-ms 200 读超时时间
eureka.server.peer-node-total-connections 1000 连接池最大活动数
eureka.server.peer-node-total-connections-per-host 500 每个host能使用的最大连接数
eureka.server.peer-node-connections-idle-timeout-seconds 30 连接池中连接的空闲时间
参数调优
  • 常见问题

为什么服务下线了,Eureka Server接口返回的信息还在
为什么服务上线了,Eureka Client不能及时收取
为什么有时候会出现

  • 解决办法
    • Eureka Server不是强一致性的,注册中心残留过期的实例信息是正常的

    应用实例会异常挂掉,没能在挂掉之前告诉Eureka Server要下线掉该服务实例信息,这个就需要依赖Eureka Server 的Eviction Task去剔除
    应用实例有告知Eureka Server下线,但是由于Eureka Server的REST API有response cache,所有需要等待缓存失效才能更新
    Eureka Server由于开启病引入了 SELF PRESERVATION 模式,导致registry信息不回因为过期而被清除,直到退出该模式

    • 针对Client下线没有通知Server的问题,可以调整Evictio Task的调度频率,比如将默认的60秒调整为5秒
    eureka.server.eviction-interval-timer-in-ms: 5000
    
    或者调整readWriteCacheMap的过期时间
    eureka.server.response.cache-auto-expiration-in-seconds:60
    
    或者关闭readOnlyCacheMap
    eureka.server.use-read-only-response-cache:false
    
    针对 SELF PRESERVATION 的问题,在测试环境可以将enable-self-preservation设置为false
    eureka.server.enable-self-preservation:false
    
    如果这样设置会提示
    在这里插入图片描述
    或者
    在这里插入图片描述

    这个地方有个很严重的问题
    在实际生产中,网络波动在所难免,经常会存在实例与Server之间的心跳检测未能如期,但是实例本身并没有问题,如果关闭了自我保护,会造成误判,甚至大面积的误判,导致服务列表的大部分注册信息被删除,无服务可用
    这也是Eureka 为什么引入SELF PRESERVATION,就是为了解决这一问题,当最近一分钟收到的续约次数小于指定阈值,就会关闭租约失效剔除,禁止定时任务剔除失效实例,从而保护注册信息,在生产环境可以把renewal-percent-threshold和lease-renewal-interval-in-seconds参数调小一些,从而提高触发SELF PRESERVATION的门槛

    eureka.instance.lease-renewal-interval-in-seconds:10    # 默认是30
    eureka.server.renewal-percent-threshold-timeout:0.49    # 默认是0.85
    
    • 针对新服务上线,Eureka Client获取不及时的问题,在测试环境可以提高Client端拉取注册信息的频率,比如将默认的30s改为5s
    eureka.clinet.registry-fetch-interval-seconds:5
    

    总结

    本章并无实际代码,主要对Eureka注册中心的介绍和相关参数的简介,欢迎补充和指错

你可能感兴趣的:(springcloud)