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 | 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 | #测试时关闭自我保护机制,保证不可用服务及时踢出 |
客户配置:
1 | #每间隔2s,向服务端发送一次心跳,证明自己依然"存活" |
Eureka Server 进入自我保护机制,会出现以下几种情况:
(1 Eureka 不再从注册列表中移除因为长时间没收到心跳而应该过期的服务
(2 Eureka 仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上(即保证当前节点依然可用)
(3 当网络稳定时,当前实例新的注册信息会被同步到其它节点中
Eureka增加权限认证(spring-security)
Spring Cloud Eureka 增加权限认证:cxytiandi.com/blog/detail/12171
通过spring-security来开始用户认证,然后在application.properties中加上认证的配置信息
1 | security.basic.enabled=true #开启认证 |
重新启动注册中心,访问 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 | Eureka Server端:关闭自我保护,并且按需配置清理无效节点的时间间隔(默认90s) |
注意:这些配置仅建议在开发或者测试使用,生产环境还是坚持使用默认值。
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 | 末配置spring.application.name或者eureka.instance.appname属性。如果这两个属性均不配置,就会导致应用名称UNKNOWN的问题。 |
微服务实例状态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
java spring系列
java_spring01读书要点
java_spring02ioc有什么优点
java_spring04Autowired与Resource差异解析
java_spring05循环依赖
java_spring06AOP
java_spring07mybatis学习要点
java_微服务01SpringCloud基础
java_微服务02SpringBoot学习笔记
java_微服务03SpringBoot常见问题
java_微服务04SpringCloud学习笔记
java_微服务06SpringCloud常见问题之Eureka
java_微服务07SpringCloud常见问题之Feign
java_微服务08SpringCloud常见问题之Hystrix
java_微服务09SpringCloud常见问题之Ribbon
java_微服务10SpringCloud常见问题之Zuul
java_微服务11SpringCloud其他问题