java_微服务06SpringCloud常见问题之Eureka

Eureka负责服务的注册与发现,如果学习过Zookeeper的话,就可以很好的理解,Eureka的角色和 Zookeeper的角色差不多,都是服务的注册和发现,构成Eureka体系的包括:服务注册中心、服务提供者、服务消费者

Eureka集群搭建

eureka 与 zookeeper的区别,什么时候用eureka?什么时候用zookeeper

Eureka Server 各个节点都是平等的,几个节点挂掉不会影响正常节点的工作,剩余的节点依然可以提供注册和查询服务。而 Eureka Client 在向某个 Eureka 注册时,如果发现连接失败,则会自动切换至其它节点。只要有一台 Eureka Server 还在,就能保证注册服务可用(保证可用性),只不过查到的信息可能不是最新的(不保证强一致性)。
eureka是AP,zookeeper是CP

主要区别:原理问题,CAP定理。
一致性 C:
这里就引用zookeeper来说明,还是举例子比较好,Zookeeper在设计的时候遵循的是CP原则,即一致性,Zookeeper会出现这样一种情况,当master节点因为网络故障与其他节点失去联系时剩余节点会重新进行leader选举,问题在于,选举leader的时间太长:30~120s,且选举期间整个Zookeeper集群是不可用的,但是zookeeper就是要保证所有的节点可用的前提下,才能正常调用服务。这就导致在选举期间注册服务处于瘫痪状态,在云部署的环境下,因网络环境使Zookeeper集群失去master节点是较大概率发生的事情,虽然服务能够最终恢复,但是漫长的选举时间导致长期的服务注册不可用。最终导致系统瘫痪一段时间。
可用性 A:
Eureka在设计的时候遵循的是AP原则,即可用性。Eureka各个节点(服务)是平等的, 没有主从之分,几个节点down掉不会影响正常工作,剩余的节点(服务) 依然可以提供注册与查询服务,而Eureka的客户端在向某个Eureka注册或发现连接失败,则会自动切换到其他节点,也就是说,只要有一台Eureka还在,就能注册可用(保证可用性), 这样的情况下最多导致某些微服务接口可能调不通,但是整个分布式架构依然正常运行,并且eureka会将不可用的服务剔除,结果就是所有的服务都可用,再结合springcloud整合的hystrix的降级功能或者zool的过滤功能,这些问题都迎刃而解。
什么时候用eureka?什么时候用zookeeper ?
举例子:比如在淘宝节那天,成千上万的访问进来,我们是用 eureka 还是 zookeeper ?比如,如果用zookeeper,在那天晚上00:00抢购的时候,zookeeper节点挂掉一部分,难道我们要等待zookeeper重新选举一个新的leader吗?情况想复杂点,如果00:00-00:30由于网络波动,zookeeper一直选举是不是淘宝节就不过了 ?当然这种情况几乎不存在,zookeeper依然能够胜任,只是理想状态。但是作为架构师,在选择上是不是就要优先考虑 可用性了。
那什么情况下,可以考虑使用zookeeper呢 ?zookeeper除了服务注册与发现的功能外,还可以用于分布式锁上,保证数据一定准确,但是在服务注册与发现的功能上,推荐使用eureka。

Eureka服务注册中心自我保护机制

自我保护机制是Eureka注册中心的重要特性,当Eureka注册中心进入自我保护模式时,在Eureka Server首页会输出如下警告信息:

1
2
3
EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE.  

emergency! eureka may be incorrectly claiming instances are up when they're not. renewals are lesser than threshold and hence the instances are not being expired just to be safe.

在没有Eureka自我保护的情况下,如果Eureka Server在一定时间内没有接收到某个微服务实例的心跳,Eureka Server将会注销该实例,但是当发生网络分区故障时,那么微服务与Eureka Server之间将无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是正常的,此时不应该注销这个微服务,如果没有自我保护机制,那么Eureka Server就会将此服务注销掉。
固定时间内大量实例被注销,可能会严重威胁整个微服务架构的可用性。为了解决这个问题,Eureka 开发了自我保护机制,那么什么是自我保护机制呢?
当Eureka Server节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么就会把这个微服务节点进行保护。一旦进入自我保护模式,Eureka Server就会保护服务注册表中的信息,不删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该Eureka Server节点会再自动退出自我保护模式。
所以,自我保护模式是一种应对网络异常的安全保护措施,它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留),也不盲目注销任何健康的微服务,使用自我保护模式,可以让Eureka集群更加的健壮、稳定。
但是Eureka Server 自我保护模式也会给我们带来一些困扰,如果在保护期内某个服务提供者刚好非正常下线了,此时服务消费者就会拿到一个无效的服务实例,此时会调用失败,对于这个问题需要服务消费者端具有一些容错机制,如重试,断路器等
Eureka的自我保护模式是有意义的,该模式被激活后,它不会从注册列表中剔除因长时间没收到心跳导致注册过期的服务,而是等待修复,直到心跳恢复正常之后,它自动退出自我保护模式。这种模式旨在避免因网络分区故障导致服务不可用的问题
Eureka Server 在运行期间会去统计心跳失败比例在 15 分钟之内是否低于 85%,如果低于 85%,Eureka Server 即会进入自我保护机制
例如,两个微服务客户端实例A和B之间有调用的关系,A是消费者,B是提供者,但是由于网络故障,B未能及时向Eureka发送心跳续约,这时候Eureka 不能简单的将B从注册表中剔除,因为如果剔除了,A就无法从Eureka 服务器中获取B注册的服务,但是这时候B服务是可用的;
所以,Eureka的自我保护模式最好还是开启它。

关于自我保护常用几个配置如下:
服务器端配置:

1
2
#测试时关闭自我保护机制,保证不可用服务及时踢出  
eureka.server.enable-self-preservation=false

客户配置:

1
2
3
4
#每间隔2s,向服务端发送一次心跳,证明自己依然"存活"  
eureka.instance.lease-renewal-interval-in-seconds=2
#告诉服务端,如果我10s之内没有给你发心跳,就代表我故障了,将我踢出掉
eureka.instance.lease-expiration-duration-in-seconds=10

Eureka Server 进入自我保护机制,会出现以下几种情况:
(1 Eureka 不再从注册列表中移除因为长时间没收到心跳而应该过期的服务
(2 Eureka 仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用)
(3 当网络稳定时,当前实例新的注册信息会被同步到其它节点中

Eureka增加权限认证(spring-security)

Spring Cloud Eureka 增加权限认证:cxytiandi.com/blog/detail/12171
通过spring-security来开始用户认证,然后在application.properties中加上认证的配置信息

1
2
3
security.basic.enabled=true #开启认证  
security.user.name=goojia #用户名
security.user.password=goojia123456 #密码

重新启动注册中心,访问 http://localhost:8761/ 此时浏览器会提示你输入用户名和密码,输入正确后才能继续访问Eureka提供的管理页面。

注意事项
注册中心开启认证后,项目中的注册中心地址的配置也需要改变,需要加上认证的用户名和密码
eureka.client.serviceUrl.defaultZone=http://用户名:密码localhostr:8761/eureka/

Spring Security导致Eureka注册失败

Spring Boot2中Spring Security导致Eureka注册失败:cxytiandi.com/blog/detail/19388
去掉Spring Security后问题解决,可以知道问题是Spring Security引起的,CSRF保护默认是开启的,可以禁用掉即可

Eureka注册服务慢(为何3次)

原因:心跳为30S
解决方式:修改心跳时间(开发环境可以使用,生产环境推荐使用默认值)
服务注册涉及到周期性心跳,默认30秒一次(通过客户端配置的serviceUrl)。只有当实例、服务器端和客户端的本地缓存中的元数据都相同时,服务才能被其他客户端发现(所以可能需要3次心跳)

为什么注册服务这么慢
1、注册延迟
Eureka Client一启动,不是马上向Eureka Server注册,它有一个延迟向Eureka Server注册的时间,也就是定期向Eureka Server发送心跳,默认是30秒。
2、Eureka Server的响应缓存
Eureka Server维护了一个每30秒更新一次的响应缓存。可通过更改配置 eureka.server.responseCacheUpdateIntervalMs来修改。所以,当某个Eureka Client刚刚注册,如果未更新到缓存,那么他不会出现在返回给客户端的注册信息列表中。
3、Eureka Server刷新缓存
Eureka Client保留了Eureka Server注册表信息到缓存,然后每30秒调用一次Eureka Server更新一次缓存。
4、在极端情况下可能需要3次心跳,Eureka Client才能发现服务
5、不要修改心跳时间间隔
可以使用eureka.instance.leaseRenewalIntervalInSeconds更改期限,这将加快客户端连接到其他服务的过程。在生产中,最好坚持使用默认值,因为服务器内部有一些计算可以 对租赁更新期进行假设。

已停止的微服务节点注销慢或者不注销

在开发测试环境下,常常希望Eureka server能迅速有效地注销已停止的微服务实例,然而由于Eureka Server清理无效节点周期长(默认90s),以及自我保护模式等,可能会遇到已停止的微服务节点注销慢或者甚至不注销的问题
解决办法:

1
2
Eureka Server端:关闭自我保护,并且按需配置清理无效节点的时间间隔(默认90s)
Eureka Client端:开启健康检查,并按需配置续约更新时间和到期时间(默认都是30s)

注意:这些配置仅建议在开发或者测试使用,生产环境还是坚持使用默认值。

Eureka集群数据同步

第一层含义:Eureka Client只会向一个Eureka Server注册服务。
一个Eureka Client只会向一个Eureka Server注册服务,如果失败,会向其他Eureka Server进行重试,
并且Eureka Client发起的注册请求是单条操作,不可能批量操作,具体实现在RetryableEurekaHttpClient.class中。
第二层含义:Eureka Server集群之间,通过复制进行数据同步。
Eureka Server集群之间的数据同步是批量操作。
Eureka 是弱数据一致性,选择了 CAP 中的 AP。
Eureka 采用 Peer to Peer 模式进行数据复制。
Eureka 通过 lastDirtyTimestamp 来解决复制冲突。
Eureka 通过心跳机制实现数据修复。

当 Eureka Server 收到客户端的注册、下线、心跳请求时,通过 PeerEurekaNode 向其余的服务器进行消息广播,如果广播失败则重试,直到任务过期后取消任务,此时这两台服务器之间数据会出现短暂的不一致。
注意: 虽然消息广播失败,但只要收到客户端的心跳,仍会向所有的服务器(包括失联的服务器)广播心跳任务
如果网络恢复正常,收到了其它服务器广播的心跳任务,此时可能有三种情况:
一是脑裂很快恢复,一切正常;
二是该实例已经自动过期,则重新进行注册;
三是数据冲突,出现不一致的情况,则需要发起同步请求,其实也就是重新注册一次,同时踢除老的实例。
总之,通过集群之间的消息广播可以实现数据的最终一致性。

SpringCloud 注册中心 Eureka 集群是怎么保持数据一致的?:https://www.pianshen.com/article/3055766665/
Spring Cloud Eureka 原理分析:https://tech.yangqianguan.com/607d1e7ece7094706059f124/
Eureka源码05-服务集群初始化和同步原理分析:www.saily.top/2020/03/28/springcloud/eureka05/

Eureka Client注册到Eureka Server的秘密

参考:Eureka Client注册到Eureka Server的秘密:https://blog.didispace.com/spring-cloud-eureka-register-detail/

Eureka Client配置对应的Eureka Server地址分别是8761、8762、8763、8764。这里存在两个问题:
Eureka Client会将自身信息分别注册到这四个地址吗?
Eureka Clinent注册机制是怎样的?
quarantineSet中保存的是(8761、8762、8763)三个Eureka Server
Eureka Server全量列表的内容是(8761、8762、8763、8764)四个Eureka Server,过滤后返回的结果为8764这个Eureka Server。
本文的例子中8761、8762、8763这三个Eureka Server的状态是down而8764这个Eureka Server的状态是UP,我们其实是想走到最后的else分支,从而完成过滤操作,并最终得到8764这个Server,遗憾的是它并不会走到这个分支,而是被上面的else if (quarantineSet.size() >= threshold)这个分支所拦截,返回的依旧是全量的Eureka Server列表。这样造成的后果就是Eureka Client依旧会依次向(8761、8762、8763)这三个down的Eureka Server发起注册请求
那么问题的关键在哪里呢?问题的关键就是threshold这个值的由来,因为此时quarantineSet.size()的值为3,而3这个值大于threshold,从而导致,会将quarantineSet集合清空,返回全量的Server列表。
因为这个值是0.66而此时全量的Eureka Server值为4。计算之后的值为2,而由于注册的for循环为3次,所以当第二次发起注册流程的时候quarantineSet的值始终大于threshold。这样就会导致一个问题,就是如果8761、8762、8763一直是down即使8764一直是好的,那么Eureka Client也不会注册成功。而且这个参数值的区间为0到1.

接下来按照这个配置再次回顾下上面的流程:
01,Eureka Client启动时进行注册(8761、8762、8763的状态是down),所以此时quarantineSet的值为3.
02,接下来在定时任务中又触发注册事件,此时因为参数的值从0.66调整为1。所以计算出的threshold的值为4。而此时quarantineSet的值为3。所以不会进入到else if (quarantineSet.size() >= threshold)分支,而是会进入最后的esle分支。
03,在else分支中会完成过滤功能,最终返回的list中的结果只有一个就是8764这个Eureka Server。
04,Eureka Client向8764这个Eureka Server发起注册请求,得到成功相应,并返回。

Eureka的UNKNOWN问题总结与解决

注册信息UNKNOWN,是新手常会遇到的情况,一种是应用名称UNKNOWN,另一种是应用状态
UNKNOWN。下面分别讨论这两种情况。
应用名称UNKNOWN
应用名称UNKNOWN显然不合适,首先是微服务的名称不够语义化,无法直观看出这是哪个微服务;
更重要的是,我们常常使用应用名称消费对应微服务的接口。
一般来说有两种情况会导致该问题的发生:

1
2
末配置spring.application.name或者eureka.instance.appname属性。如果这两个属性均不配置,就会导致应用名称UNKNOWN的问题。
某些版本SpringFox会导致该问题,例如SpringFox 2.6.0.建议使用SpringFox 2.6.1或更新版本。

微服务实例状态UNKNOWN
微服务实例状态UNKNOWN同样很麻烦。一般来讲,只会请求状态是UP的微服务。该问题一般由健康检查所导致。
eureka.client.healthcheck.enabled=true必须设置在application.yml中,而不能设置在bootstrap.yml中,否则一些场景下会导致应用状态UNKNOW的问题。

EnableEurekaClient 和 EnableDiscoveryClient

在不同的应用程序中,有人使用 EnableEurekaClient,有人使用 EnableDiscoveryClient,都能作为 Eureka Client 的注解。有何区别呢?
服务发现是有多实现方式,即:多种组件可以作为注册中心,eureka,consul,zookeeper,@EnableEurekaClient 是在spring-cloud-netflix中,仅仅适合于 Eureka 注册中心。@EnableDiscoveryClient 是在spring-cloud-commons中,适合 Eureka、Consul、Zookeeper 注册中心。在使用时要注意使用依赖的包,否则会发现找不到该注解。
参考:https://stackoverflow.com/questions/31976236/whats-the-difference-between-enableeurekaclient-and-enablediscoveryclient

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×