目录
概述
介绍
Euraka基本配置
Eureka-server配置参数
基本参数
Response cache参数
peer相关参数
http参数
基本参数
instance参数
http参数
其他参数
问题及分析
单机器多个实例配置问题
个主机上启动多个实例
健康监测及端点配置,euraka注册页点击服务连接Error
对于健康检查的有效配置参数
自我保护失效问题
自注册问题
改变首次注册时间长问题
缓存
服务器缓存
Time lag,服务上线下线延迟
time lang问题
感知公式
建议配置
Server端
Client端
现在很多项目使用springcloud euraka做为注册中心,现阶段准备统一注册中心,趁现在时间还有空余,就把项目所遇到的问题,及euraka配置参数,统一记录下,以做备忘及后续员工的技术手册。
euraka做为微服务架构里的注册中心,保证服务高可用性和最终一致性,搭建简单,但在正产环境应用还是需要注意很多问题。
Spring-Cloud Euraka是Spring Cloud集合中一个组件,它是对Euraka的集成,用于服务注册和发现。Eureka是Netflix中的一个开源框架。它和 zookeeper、Consul一样,都是用于服务注册管理的,同样,Spring-Cloud 还集成了Zookeeper和Consul。
在项目中使用Spring Cloud Euraka的原因是它可以利用Spring Cloud Netfilix中其他的组件,如zull等,因为Euraka是属于Netfilix的。
Eureka由多个instance(服务实例)组成,这些服务实例可以分为两种:Eureka Server和Eureka Client。为了便于理解,我们将Eureka client再分为Service Provider和Service Consumer。
eureka.server.
#是否开启自我保护模式,默认是开启的
#每分钟需要收到的续约次数的阈值, server会根据某个应用注册时实例数,计算每分钟应收到的续约次数,若收到的次数少于该阈值,server会关闭该租约,并禁止定时任务剔除失效的实例,保护注册信息
#指定updateRenewalThreshold定时任务的调度频率,来动态更新expectedNumberOfRenewsPerMin和numberOfRenewsPerMinThreshold值
# 心跳阈值重新计算周期,可适当调短些
#指定EvictionTask定时任务的调度频率,用于剔除过期的实例,默认是60秒执行一次
#若有效服务挂掉,并没有告知euraka server,通过EvictionTask任务剔除。
eureka.instance.registry.
#指定每分钟需要收到的续约次数值,实际该值在其中被写死为count*2
Euraka server为了提升自身REST API接口的性能,提供了2个缓存,一个是基于ConcurrentHashMap的readOnlyCacheMap,一个是基于Guava Cache的readWriteCacheMap。
eureka.server.
#是否使用只读的response-cache。
#设置CacheUpdateTask的调度时间间隔,用于从readWriteCacheMap更新数据到readOnlyCacheMap,仅仅在eureka.server.use-read-only-response-cache为true的时候才生效。
#设置readWriteCacheMap的expireAfterWrite参数,指定写入多长时间后过期。
eureka.server.
#默认10分钟,指定peersUpdateTask调度的时间间隔,用于从配置文件刷新peerEurekaNodes节点的配置信息(eureka.client.serviceUrl相关zone的配置)。
#指定更新peer nodes状态信息的时间间隔。
eureka.server.
#server各node间连接超时时长,默认200毫秒,200毫秒没连接上server的其他节点,就会认为该node不可用
#从其他节点读取数据超时时间 ,默认200毫秒
#server的单个node连接池最大的活动连接数
#server的单个node每个hot能使用的最大连接数
#server的node连接池连接的空闲时间
eureka.client.
#告知Client有哪些region和availability-zones,支持配置修改运行时生效
#是否过滤出注册到eureka中所有InstanceStatus为UP的实例,默认为true
#是否将该实例注册到 eureka server,eureka注册中心配置为false,不把自身注册到eureka
#是否优先使用与该实例处于相同zone的Eureka server ,默认为true
#即默认会使用与实力处于相同zone的server,如果找不到,才会默认使用defaultZone中配置的。
#是否将本地实例状态通过ApplicationInfoManager实时同步到到Eureka Server中,默认是true,一般情况下不要改,默认就行
#变更同步到Eureka Server的时间间隔
#从Eureka服务端获取注册信息的间隔时间,单位为秒
#初始化实例信息到Eureka服务端的间隔时间,单位为秒,单位为秒
#是否从Eureka服务端获取注册信息
#指定注册中心
eureka.instance.
#指定该应用实例的元数据信息。
#是否优先使用IP地址来代替host name,默认是false。
#指定Eureka Client间隔多久向Eureka Server发送心跳来告知Eureka Server该实例还存活,默认是90秒。
# Eureka Client向Server发送心跳的时间间隔,默认CLient隔30秒就会向Server发送一次心跳。
#客户端首次注册时间是30S,首次注册与首次心跳绑定,首次心跳发送后会收到not found的响应,若要加快首次注册速度,可查看1.2.1基本参数
eureka.client.
#连接server的超时时间 默认5秒
# client 从server读取数据超时时间,默认8秒
#连接池最大的活动连接数 最大默认200个连接数
#每个host能使用的最大连接数 ,默认每个主机最多只能使用50个连接
#连接池中连接的空闲时间
#轮询Eureka服务端地址更改的间隔时间,单位为秒。当我们与Spring CLoud Config整合,动态刷新Eureka的serviceURL地址时需要关注该参数
eureka.client.
#心跳连接池的初始化线程数
#心跳超时重试延迟时间的最大乘数值
#缓存刷新线程池的初始化线程数
#缓存刷新重试延迟时间的最大乘数值。
#使用DNS来获取Eureka服务端的serviceUrl
Eureka的配置中,针对统一主机中启动多实例的情况,对实例名的默认命名作了更为合理的扩展,它采用了如下默认规则:
${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${server.port}}
对于实例名的命名规则,我们也可以通过eureka.instance.instanceId参数来进行配。如:
eureka.instance.instanceId=${spring.application.name}:${random.int}
通过上面的配置,利用应用名加随机数的方式来区分不同的实例,从而实现在统一主机上,不指定端口就能轻松启动多个实例的效果。
home-page-url:应用主页的URL
status-page-url:状态页的URL
health-check-rl:健康检查的URL
状态页和健康检查的URL在Spring Cloud Eureka中默认使用了spring-boot-actuator模块提供的/info端点和/health端点。但并不代表Eureka各个服务实例的健康监测通过spring-boot-actuator模块/health端点来实现;他是依靠依靠客户端心跳的方式来保持服务实例的存活,我们需要配置为actuator来实现健康检查。
为了服务的正常运作,我们必须确保Eureka客户端的/health端点在发送元数据的时候,是一个能被注册中心访问到的地址,否则服务注册中心不会根据应用的健康检查来更改状态(仅当开启了healthcheck功能时,以该端点信息作为健康检查标准)。而/info端点如果不正确的话,会导致在Eureka面板中单机服务实例时,无法访问到服务实例提供的信息接口。 大多数情况下,我们并不需要修改这几个URL配置。
需要配置eureka.client.healthcheck.enabled=true
健康检查参数如下:
参数名 |
说明 |
默认值 |
preferIpAddress |
是否优先使用IP地址作为主机名的标识 |
FALSE |
leaseRenewalIntervalInSeconds |
Eureka客户端向服务端发送心跳的时间间隔,单位为秒 |
30 |
leaseExpirationDurationInSeconds Eureka |
服务端在收到最后一次心跳之后等待的时间上限,单位为秒。超过该时间之后服务端会将该服务实例从服务清单中剔除,从而禁止服务调用请求被发送到该实例上 |
90 |
nonSecurePort |
非安全的通信端口号 |
80 |
securePort |
安全的通信端口号 |
443 |
nonSecurePortEnabled |
是否启用非安全的通信端口号 |
TRUE |
securePortEnabled |
是否启用安全的通信端口号 |
|
appname |
服务名,默认取spring.application.name的配置值,如果没有则为unknown |
|
hostname |
主机名,不配置的时候讲根据操作系统的主机名来获取 |
|
修改client的心跳时间,会导致自我保护失效。
lease-renewal-interval-in-seconds: 30
eureka server认为客户端是以30s的频率发送心跳,服务器端收到的最大心跳时间是:N个instance * 2(60s/30s)* threshold
即若有2个服务注册,2*2*85%=3.4个心跳
若改为15S,1个注册服务1分钟会发送4次心跳,而euraka server 的阈值还是3次心跳,那么自我保护模式失效。
核心原因就是在Eureka Server计算期望心跳数的时候写死了每分钟的心跳间隔,即30秒,所以他永远会是*2(60s/30s)
Euraka启动时:
eureka-core-1.4.12-sources.jar!/com/netflix/eureka/registry/PeerAwareInstanceRegistryImpl.java
client注册时:
eureka-core-1.4.12-sources.jar!/com/netflix/eureka/registry/AbstractInstanceRegistry.java
由于eureka-core-1.4.12版本里头,你去调整eureka.instance.leaseRenewalIntervalInSeconds的话,代码里头没有相应调整factor,也就是代码还是60/30=2,所以会破坏eureka内置的设计思路。不过对于小型项目来说,没有跨机房,网络没有那么恶劣的话,想避免自我保护导致的服务注册列表不能修改的问题,可以选择以下任一方式尝试下:
调小eviction-interval-timer-in-ms: 5000,清理间隔(单位毫秒, 默认是60*1000)
单机模式下:
register-with-eureka和fetch-registry应为false,否则启动会报错:Cannot execute request on any known server。原因,在默认设置下,eureka服务注册中心会将自己作为客户端来尝试注册自己。
集群模式下:
fetch-registry与register-with-eureka,一定要设置为true或者不写,否则会出现unavailable-replicas
并且尽量把所有的euraka服务地址都要写在客户端defaultzone下。
首次注册和首次心跳是绑定在一起的,首次心跳发送后会收到not found的响应,client识别还没有注册过,client会马上注册。
由参数initiall-instance-info-replication-interval-seconds: 30控制,可适当调整此参数加快首次注册过程。他是控制首次改变实例状态(UP/DOWN )的时间,启动的时候状态肯定是需要改变的,所以他可以用来加快首次注册速度,并且改变这个值不会影响到保护模式
如果使用的是springcloud-euraka的话,没有这种问题。
Eureka内部的缓存分很多级,主要有registry、readWriterCacheMap、readOnlyCacheMap;另外还有一个维护最近180s增量的队列recentlyChangedQueue。
写操作
包括注册、取消注册等,都直接操作在registry上,同时也会更新recentlyChangedQueue和readWriterCacheMap
读操作
读默认是从readOnlyCacheMap读取,读不到的话再从readWriterCacheMap,若还不命中,从registry中取。
滥用缓存的读操作
这个读操作的三级缓存结构,非常让人困惑,registry已经是ConcurrentHashMap,纯内存操作,性能非常高了,为什么前面还要加两级缓存;readWriterCacheMap的数据是在写入以后responseCacheAutoExpirationInSeconds(默认180)秒内失效,readOnlyCacheMap则是一个定时任务,每responseCacheUpdateIntervalMs(默认30)秒从readWriterCacheMap获取最新数据
去掉readOnlyCacheMap
从CAP理论上看,Eureka是一个AP系统,但是在C层面这么弱,就是因为各种无谓的缓存造成的,看了下readWriterCacheMap去掉比较难,但是readOnlyCacheMap有一个开关use-read-only-response-cache。
服务端默认会有个read only response cache,每30秒更新一次,通过response-cache-update-interval-ms控制,注册后需要等待刷新后才能看到。(通过rest api不能看到,但是你可以在web ui上看到,因为ui没有缓存)
了解time lag之前,先看几个问题及解决方案。
Client 下线没有通知 Eureka Server 的问题:
应用实例异常挂掉,没能在挂掉之前告知Eureka server要下线掉该服务实例信息。这个就需要依赖Eureka server的EvictionTask去剔除
针对 response cache 的问题:
可以根据情况考虑关闭 readOnlyCacheMap
use-read-only-response-cache: false
# 设置read Write CacheMap的expire After Write参数,指定写入多长时间后过期
# 有效防止的问题是:应用实例下线时有告知Eureka server下线,但是由于Eureka server的REST API有response cache,因此需要等待缓存过期才能更新
response-cache-auto-expiration-in-seconds: 60
Eureka wiki中提到的2min time lag问题,其实分多个角度看,不一定是120s。
服务正常上线/修改,最大可能会有120s滞后。
30(首次注册 init registe) + 30(readOnlyCacheMap)+30(client fetch interval)+30(ribbon)=120
如果是在Spring Cloud环境下使用这些组件(Eureka, Ribbon),不会有首次注册30秒延迟的问题,服务启动后会马上注册,所以从注册到发现,最多可能是90s。
服务异常下线:
最大可能会有270s滞后
定时清理任务每eviction-interval-timer-in-ms(默认60)执行一次清理任务,每次清理任务会把90秒(3个心跳周期,eureka.instance.lease-expiration-duration-in-seconds)没收到心跳的踢除,但是根据官方的说法 ,因为代码实现的bug,这个时间其实是两倍,即180秒,也就是说如果一个客户端因为网络问题或者主机问题异常下线,可能会在180秒后才剔除。 读取端,因为readOnlyCacheMap以及客户端缓存的存在,可能会在30(readOnlyCacheMap)+30(client fetch interval)+30(ribbon)=90,所以极端情况最终可能会是180+90=270
异常下线最长感知时间计算公式:
2*eureka.instance.leaseExpirationDurationInSeconds+ eureka.server.evictionIntervalTimerInMs+ eureka.client.registryFetchIntervalSeconds+ ribbon. ServerListRefreshInterval
正常上下线感知公式:eureka.client.registryFetchIntervalSeconds+ribbon. ServerListRefreshInterval
## 中小规模下,自我保护模式坑比好处多,所以关闭它
eureka.server.enable-self-preservation=false
## 心跳阈值计算周期,如果开启自我保护模式,可以改一下这个配置
## eureka.server. renewal-threshold-update-interval-ms:120000
## 主动失效检测间隔,配置成5秒
eureka.server.eviction-interval-timer-in-ms=5000
## 心跳间隔,5秒
eureka.instance. lease-renewal-interval-in-seconds=5
## 没有心跳的淘汰时间,10秒
eureka.instance. lease-expiration-duration-in-seconds=10
## 禁用readOnlyCacheMap
eureka.server. use-read-only-response-cache=false
## 心跳间隔,5秒
eureka.instance.lease-renewal-interval-in-seconds=5
## 没有心跳的淘汰时间,10秒
eureka.instance.lease-expiration-duration-in-seconds=10
# 定时刷新本地缓存时间
eureka.client.registry-fetch-interval-seconds=5
# ribbon缓存时间
ribbon.server-list-refresh-interval=2000