上一篇《Eureka使用详解》一文中我们详细介绍了什么是Spring Cloud微服务。Spring Cloud中Service注册中心及其Client如何实现并如何完成服务注册的。这一篇我们将介绍Eureka注册中心的高可用如何实现及其使用中的注意事项。
一. 序言
微服务就是开发一组小型服务的方式来完成对系统整个开发。其中每个小微服务都独立运行在自己的进程中,这些服务是围绕业务功能构建一个小微服务集群。其最终的目的就是:高容错、可扩展、易部署。但当我们在Eureka只采用单台注册服务中心时,如果注册中心服务崩溃,就会造成整个服务瘫痪。为解决这种风险。Eureka提供高可用的技术方案,下面将具体演示如果实现Eureka注册中心的高可用。
二. 什么是高可用性
高可用(High Availability)指通过尽量缩短因日常维护操作和突发的系统崩溃所导致的停机时间,以提高系统和应用的可用性。这也是和不间断操作的容错技术不同。高可用是为了防止核心计算机应用系统因故障突然停机的一种可靠的手段。
三. 什么是CAP
CAP原则又称CAP定理,指的是一个分布式中,一致性(Consistency)、可用性(Availability)、分区容错(Partition-tolerance).一般在一个分布式系统中三个要素不可同是具有,只能选择其中两个,具体如图
- 一致性(Consistency)
在分布式系统中,所有节点在同一时刻的数据都是一致的。 - 可用性(Availability)
在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。即每个请求不管成功与否都能得到响应。 - 分区容错(Partition-tolerance)
保证系统中任意信息的丢失都不会影响系统的运行。
一个分布式系统里面,节点组成的网络本来应该是连通。然而可能因为一些故障,使得有些节点之间不连通,整个网络就分成了几块区域。数据就散布在了这些不连通的区域中。这就叫分区。
- AP
保持可用性和分区容错。如果选择分区容错性和可用性,当节点损坏时,遇到分区事件,受影响的服务不需要等待数据一致,就可以对外提供服务,保证了可用性就必须放弃一致性。 - CP
保证一致性和分区容错。如果选择分区容错性和一致性,为保证一致性,各个节点之间的数据必须保持一致,当出现分区时,会导致同步时间无限延长,在同步的这段时间就无法对外提供服务,就无法保证可用性。 - CA
保持一致性的同时保证可用性,这在分布式系统中是不存在的,因为分区问题总是会出现在分布式系统中。出现分区就必然会产生一致性问题和可用性问题,一致性和可用性两者只能选其一。
四. Eureka集群部署原理
- 原理
- Eureka Client内置一个 使用轮询负载算法的负载均衡器。服务启动后,Eureka Client将会向Eureka Server发送心跳更新服务,如果Eureka Server在多个心跳周期内没有接收到某个服务的心跳,Eureka Server将会从服务注册表中把这个服务节点移除(默认90秒)。
- Eureka Server是基于netflix设计出来的,其服务注册表中将会存储所有可用服务节点的信息,支持region和availabilityZone的概念,采用peer to peer的架构模式,也可以通过配置remoteRegionUrlsWithName来支持拉取远程的region实例,如果当前的region挂了,会自动fallback到远程的region获取数据,同时服务端采用renew租约和定时心跳的方式保护注册信息(self preservation机制)。
四、Eureka集群部署案例实现
- Eureka单机服务部署已在【Spring Cloud系列】- Eureka知识详解中讲解如何使用,在这基础上我们构建另外两个服务:Server-node-01、Server-node-02
- 更新配置文件(application.yml)
- eureka.client.register-with-eureka :表示是否将自己注册到Eureka Server,默认为true。
eureka.client.fetch-registry
:表示是否从Eureka Server获取注册信息,默认为trueeureka.client.serviceUrl.defaultZone
:设置与Eureka Server交互的地址,查询服务和注册服务都需要依赖这个地址。默认是http://localhost:30000/eureka ;多个地址可使用 , 分隔。
- Server-node-01配置文件
- Server-node-02配置文件
- 更新Hosts文件
、 - 更新eureka-client配置
在生产中我们可能需要三台或者大于三台的注册中心来保证服务的稳定性,配置的原理其实都一样,将注册中心分别指向其它的注册中心。这里只介绍三台集群的配置情况,其实和双节点的注册中心类似,每台注册中心分别又指向其它两个节点即可,使用application.yml来配置。 - 启动eureka-client服务轮询注册到服务端配置
五、Eureka集群测试效果
- Serve-30000服务
- Serve-30001服务
- Serve-30002服务
六、Eureka 的数据同步方式
6.1. 复制方式
分布式系统的数据在多个副本之间的复制方式,主要有主从复制和对等复制
- 主从复制
就是 Master-Slave 模式,有一个主副本,其他为从副本,所有写操作都提交到主副本,再由主副本更新到其他从副本。
写压力都集中在主副本上,是系统的瓶颈,从副本可以分担读请求。 - 对等复制
就是 Peer to Peer 模式,副本间不分主从,任何副本都可以接收写操作,然后每个副本间互相进行数据更新。
对等复制模式,任何副本都可以接收写请求,不存在写压力瓶颈,但各个副本间数据同步时可能产生数据冲突。
Eureka 采用的就是 Peer to Peer 模式
6.2. 同步过程
Eureka Server 本身依赖了 Eureka Client,也就是每个 Eureka Server 是作为其他 Eureka Server 的 Client。
Eureka Server 启动后,会通过 Eureka Client 请求其他 Eureka Server 节点中的一个节点,获取注册的服务信息,然后复制到其他 peer 节点。
Eureka Server 每当自己的信息变更后,例如 Client 向自己发起注册、续约、注销请求, 就会把自己的最新信息通知给其他 Eureka Server,保持数据同步。
如果自己的信息变更是另一个Eureka Server同步过来的,这是再同步回去的话就出现数据同步死循环了。
Eureka Server 在执行复制操作的时候,使用 HEADER_REPLICATION
这个 http header 来区分普通应用实例的正常请求,说明这是一个复制请求,这样其他 peer 节点收到请求时,就不会再对其进行复制操作,从而避免死循环。
还有一个问题,就是数据冲突,比如 server A 向 server B 发起同步请求,如果 A 的数据比 B 的还旧,B 不可能接受 A 的数据,那么 B 是如何知道 A 的数据是旧的呢?这时 A 又应该怎么办呢?
数据的新旧一般是通过版本号来定义的,Eureka 是通过 lastDirtyTimestamp 这个类似版本号的属性来实现【lastDirtyTimestamp是注册中心里面服务实例的一个属性,表示此服务实例最近一次变更时间】
比如 Eureka Server A 向 Eureka Server B 复制数据,数据冲突有2种情况:
- A 的数据比 B 的新,B 返回 404,A 重新把这个应用实例注册到 B。
- A 的数据比 B 的旧,B 返回 409,要求 A 同步 B 的数据。
还有一个重要的机制:hearbeat 心跳,即续约操作,来进行数据的最终修复,因为节点间的复制可能会出错,通过心跳就可以发现错误,进行弥补。
七、Eureka中元数据
Eureka的元数据有两种:标准元数据和自定义元数据
- 标准元数据
主机名、IP地址、端口号、状态页和健康检查等信息,这些信息都会被发布在服务注册表中,用于服务之间的调用。 - 自定义元数据
可以使用eureka.instance.metadata-map配置,这些元数据可以在远程客户端中访问,但是一般不改变客户端行为,除非客户端知道该元数据的含义
元数据在程序中的应用
- application中配置
eureka: instance: #标准元数据 preferIpAddress: true ipAddress: 127.0.0.1 #自定义元数据信息 metadata-map: name-serve:goyeer-serve serve-code: v202306
- 程序代码获取Eureka配置的元数据
@Autowired DiscoveryClient discoveryClient; @RequestMapping("testDiscovery") public String testDiscovery() { List<ServiceInstance> serviceInstanceList = discoveryClient.getInstances("cloud-provider"); // 获取第一个微服务实例的元数据信息 ServiceInstance serviceInstance = serviceInstanceList.get(0); //打印微服务实例的元数据信息 System.out.println(serviceInstance); return "testDiscovery"; }
八、Eureka自我保护
- 什么是自我保护
服务提供者会定期的向注册中心通信续约自己;如服务提供者和注册中心之间的网络有点问题,不代表服务提供者不可用,不代表服务消费者无法访问服务提供者;如果在15分钟内超过85%的客户端节点都没有正常的心跳,那么Eureka就认为客户端与注册中心出现了网络故障,Eureka Server自动进入自我保护机制。 - 为什么会有自我保护机制默认情况下,如果Eureka Server在一定时间内(默认90秒)没有接收到某个微服务实例的心跳, Eureka Server将会移除该实例。但是当网络分区故障发生时,微服务与Eureka Server之间无法正常通 信,而微服务本身是正常运行的,此时不应该移除这个微服务,所以引入了自我保护机制。当处于自我保护模式时:
- 不会剔除任何服务实例(可能是服务提供者和EurekaServer之间网络问题),保证了大多数服务依 然可用;
- Eureka Server仍然能够接受新服务的注册和查询请求,但是不会被同步到其它节点上,保证当前节点依然可用,当网络稳定时,当前Eureka Server新的注册信息会被同步到其它节点中;
- 在Eureka Server工程中通过eureka.server.enable-self-preservation配置可用关停自我保护,默认值是打开的
eureka: server: #是否开启自我保护,生产环境建议开启,默认开启 enableSelfPreservation: true
经验:建议生产环境打开自我保护机制
九、Eureka中常用注解
- @EnableEurekaClient与@EnableDiscoveryClient
- 相同点
@EnableDiscoveryClient和
@EnableEurekaClient 共同点就是:都是能够让注册中心能够发现,扫描到改服务; - 不同点
@EnableEurekaClient只适用于Eureka作为注册中心
@EnableDiscoveryClient可以是其他注册中心如Zookeeper
十、Eureka总结
- AP: 非强一致, 要求客户端支持负载均衡及失败重试
- 对等复制:每个节点都接受写操作, 节点之间相互同步数据,当数据冲突时,通过比较节点数据的版本号 lastDirtyTimestamp 来同步最新的数据
- Zone, Region :默认数据同步只会发生在 Region 下面的多个 Zone 之间, 跨 Region 不会同步
- self preservation:eureka server 收不到服务心跳有两种情况:
一种是个别服务挂了,就走服务续约失效剔除的机制;如果 Eureka Server 默认90秒没有接收到某个微服务实例的心跳,Eureka Server 将会剔除该实例。
另一种情况就是微服务实例正常,但由于网络分区故障没收到心跳,这种情况就不应该剔除服务,所以就有了自我保护机制,如果在 15 分钟内超过 85% 的客户端节点都没有正常的心跳,Eureka Server 自动进入自我保护机制, 就会关闭服务续约失效剔除的机制