注:(版本:spring.cloud.version:Greenwich.RC2 spring.boot.version:2.1.2.RELEASE)
注:(源码:https://github.com/paulzhangcc/spring-cloud-quick-start)
1. 单机配置
@EnableEurekaServer
@EnableDiscoveryClient(autoRegister = false)
// server端代码中自动初始化client 所以这里可以设置为false EurekaServerAutoConfiguration#peerAwareInstanceRegistry
server.port=8761
#如果是在同一机器上部署多个server构成集群,会因为InetUtils获取到相同的hostname导致集群同步中相同的hostname不进行同步,可以设置eureka.server.my-url或者eureka.instance.hostname my-url优先级更高
#eureka.instance.hostname=
#eureka.server.my-url=http://localhost:8761/eureka/
spring.application.name=eureka-server
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
#关闭自我保护
eureka.server.enable-self-preservation=false
#如何判断过期com.netflix.eureka.lease.Lease#isExpired(long)过期就清楚,这个是不会和相邻节点同步,有自身定时任务去更新com.netflix.eureka.registry.AbstractInstanceRegistry.EvictionTask
eureka.server.eviction-interval-timer-in-ms=60000
eureka.server.use-read-only-response-cache=false
#eureka.server.response-cache-update-interval-ms=5000
#eureka.server.response-cache-auto-expiration-in-seconds=180
#判断Eureka peer更新 //可以动态添加server 到集群 手动添加:org.springframework.cloud.netflix.eureka.EurekaClientConfigBean.getServiceUrl
eureka.server.peer-eureka-nodes-update-interval-ms=5000
2. 集群配置
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/,http://localhost:8762/eureka/
注意:
- 对于是否需要注册到defaultZone中取决你的Server服务要提供业务服务
- 对于是否需要fetch到eureka.client.fetch-registry对于集群可以在初始化时同步获取相邻节点的注册信息代码位置:EurekaServerBootstrap方法initEurekaServerContext 中会调用(this.registry.syncUp();)去同步eurekaClient获取到的8762注册中心的数据,因为当前机器8761还没有启动web服务
- 集群节点server同步时通过service-url.defaultZone获取, 这里写不写自身无所谓,因为代码可以排除自身com.netflix.eureka.cluster.PeerEurekaNodes#isThisMyUrl
- 为了消费客户端可以及时的获取不使用response-cache,关闭了自我保护,调小了过期任务
- 调小eureka.server.eviction-interval-timer-in-ms 对于出现网络隔离,或者非正常的关闭服务,可以主动将服务及时下线
3. 启动过程
- @EnableEurekaServer添加Marker bean使得EurekaServerAutoConfiguration,EurekaServerInitializerConfiguration可以允许注册BeanDefinition
- eurekaClient初始化在EurekaServerAutoConfiguration#peerAwareInstanceRegistry中调用了
this.eurekaClient.getApplications(); // force initialization - DefaultEurekaServerContext中@PostConstruct注解的方法initialize. 1:peerEurekaNodes.start()开启动态更新集群节点列表 2:PeerAwareInstanceRegistryImpl#init初始化ResponseCache,开启了心跳阈值定时任务,用于更新预期客户端数量和每分钟最小心跳数,为自我保护机制提供对比条件,在开启自我保护情况,当上一分钟的客户端心跳数少于每分钟最小心跳数则跳过过期任务执行不删除服务的过期列表,以保证网络分区情况下server可以提供服务列表,即使此时提供的数据是错误的
- EurekaServerInitializerConfiguration实现了SmartLifecycle接口在AbstractApplicationContext#finishRefresh中触发DefaultLifecycleProcessor#startBeans,然后单独开启想成调用EurekaServerBootstrap#contextInitialized触发,主要进行1:复制的相邻server节点的注册列表,更新预期客户端数量以及心跳最小阀值 2:开启统计上一分钟心跳次数的更新任务,每分钟将当前的值放入lastBucket,并将currentBucket重置为0 3: 开启过期任务执行,判断是否自我保护,如果自我保护则停止,否则继续获取所有当前的客户端,判断client的是否过期(currentTimeMillis > lastUpdateTimestamp+duration+additionalLeaseMs)并清除过期的客户端
-
server启动之后,通过tomcat提供restful服务主要提供客户端和同步server进行服务列表的查询,更新,取消,状态更新,取消状态更新,具体的服务列表如下:
image.png -
服务端在接受到客户端的请求后,更新自身节点的内容也会对除自身之后的其他集群进行节点进行信息同步:PeerAwareInstanceRegistryImpl#replicateInstanceActionsToPeers
image.png
4. 常见问题
1.为什么同步机制只会传播一次:对于resuful接口中存在@HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication该表示判断是否需要同步,服务端接受的请求方如下图默认使用的是

image.png
节点同步使用JerseyReplicationClient这个会在webResource.header(PeerEurekaNode.HEADER_REPLICATION, "true");所以不会出现A ->B->C,所以在集群配置的时候要特别注意,所有集群节点要有相同的eureka.client.service-url.defaultZone
- 为什么要有OverriddenStatus这个状态,如何使用它做优雅的服务下线
1.deleteStatusOverride删除OverriddenStatus
2.cancel 删除OverriddenStatus
3.register 判断overriddenInstanceStatusMap是否存在,如果存在设置OverriddenStatus,对于status会通过3个规则(new FirstMatchWinsCompositeRule(new DownOrStartingRule(),
new OverrideExistsRule(overriddenInstanceStatusMap), new LeaseExistsRule());)设置,DownOrStartingRule判断registerInstance OverrideExistsRule判断overriddenInstanceStatusMap是否存在
4.statusUpdate:设置OverriddenStatus
5.Heartbeat:将OverriddenStatus复制给status,接着当参数lastDirtyTimestamp比较晚,参数OverriddenStatus不为空且不等于UNKNOWN,则会将设置参数新OverriddenStatus,下次心跳更新status
利用以上我们可以优雅的服务下线:先设置OverriddenStatus为OUT_OF_SERVICE, 当服务没有流量的时候关闭,如果是正常关闭会调用cancel,如果是非正常关闭,服务上线后需要statusUpdate
对于overriddenInstanceStatusMap默认1小时过期 - 当修改了客户端lease-renewal-interval-in-seconds(心跳的间隔)和lease-expiration-duration-in-seconds(客户端多长时间不发心跳服务端可以删除)时在开启自我保护的时候也需要重新记得设置服务expectedClientRenewalIntervalSeconds这个值默认30否则会造成自我保护开启,服务更新不及时
- lastDirtyTimestamp的作用:
1.deleteStatusOverride:更新为最晚的LastDirtyTimestamp
2.cancel 无操作
3.Heartbeat 使用lastDirtyTimestamp判断是否要设置OverriddenStatus
4.statusUpdate 更新为最晚的LastDirtyTimestamp
5.register:当存在的Instance的lastDirtyTimestamp大于registrationLastDirtyTimestamp,该注册为registration变为Instance

