前面介绍了一个最基本的Eureka Server 和Eureka Client,它是整个springcloud生态服务注册于发现的一个缩影
这里举的shi是使用java语言的client例子,但是Eureka Server提供了REST API,允许其他语言的应用通过HTTP REST的方式接入到Eureka的服务注册发现中
操作 | 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格式 |
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>
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工程
类 | 作用 |
---|---|
InstanceInfo | 代表注册的服务实例 |
LeaseInfo | 标识应用实例的租约信息 |
ServiceInstance | 包含一些通用的方法,包括优雅的关闭Eureka Client |
InstanceStatus | 服务的状态 |
本质上就是在服务启动的时候,需要调用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使用的是心跳检测
对于分布式系统来说,由于网络的不可控,出现分区是不可避免的,所以分布式系统设计一般在AP或者CP中选择
Eureka是部署在AWS的背景下设计的,网络问题不可避免,所以选择AP
在实际生产中,服务注册中心保留可用及过期的数据总比丢失掉可用的数据要好,这样的话,集群的信息就不是强一致性的,这就需要客户端能够支持负载均衡和失败重试,在Netflix的生态中,由ribbon提供此功能
一般来说,分布式应用中多个副本的复制方式,可分为主从复制和对等复制
也就是最为常见的 master-slave模式
有一个主副本,其他都是从副本,对所有的写操作都提交到主副本,再由主副本同步到其他的从副本
写操作的压力都在主副本上,是整个系统的瓶颈,从副本可以分担读请求压力
副本之间不分主从,任何副本都可以进行读写操作,然后每个副本之间进行数据同步,不存在某一块陷入瓶颈
Eureka Server 采用的就是Peer to Peer模式
各个副本之间的数据同步和冲突解决是个大问题
peer节点之间的数据相互复制并不能一定成功,因此Eureka还通过实例于Server之间的心跳检测来进行数据的最终修复,即如果发现不一致,应用实例重新进行register操作
参数 | 默认值 | 说明 |
---|---|---|
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 |
参数 | 默认值 | 说明 |
---|---|---|
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发送心跳的时间间隔 |
参数 | 默认值 | 说明 |
---|---|---|
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 | 连接池中连接的空闲时间 |
参数 | 默认值 | 说明 |
---|---|---|
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定时任务的频率,用于剔除过期的实例 |
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参数,指定写入多久后过期 |
参数 | 默认值 | 说明 |
---|---|---|
eureka.server.peer-eureka-nodes-update-interval-ms | 10分钟 | 指定peerupdateTask的调度时间间隔,用于配置文件刷新peerEurekaNodes节点的配置信息 |
eureka.server.peer-eureka-status-refresh-time-interval-ms | 30*1000 | 指定更新peer nodes状态更新的时间间隔 |
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 的Eviction Task去剔除
应用实例有告知Eureka Server下线,但是由于Eureka Server的REST API有response cache,所有需要等待缓存失效才能更新
Eureka Server由于开启病引入了 SELF PRESERVATION 模式,导致registry信息不回因为过期而被清除,直到退出该模式
eureka.server.eviction-interval-timer-in-ms: 5000
或者调整readWriteCacheMap的过期时间eureka.server.response.cache-auto-expiration-in-seconds:60
或者关闭readOnlyCacheMapeureka.server.use-read-only-response-cache:false
针对 SELF PRESERVATION 的问题,在测试环境可以将enable-self-preservation设置为falseeureka.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.clinet.registry-fetch-interval-seconds:5
本章并无实际代码,主要对Eureka注册中心的介绍和相关参数的简介,欢迎补充和指错