仅供自学回顾使用,请支持javaGuide原版书籍。
1.CAP理论
1.1.最初的理论
CAP 也就是 Consistency(一致性)、Availability(可用性)、Partition Tolerance(分区容错性) 这三个单词首字母组合。
最先该理论比较广泛的说法是:对于一个分布式系统来说,当设计读写操作时,只能同时满足以下三点中的两个:
- 强一致性(Consistency) : 所有节点访问同一份最新的数据副本
- 可用性(Availability): 非故障的节点在合理的时间内返回合理的响应(不是错误或者超时的响应)。
- 分区容错性(Partition Tolerance) : 分布式系统出现网络分区的时候,仍然能够对外提供服务。
什么是网络分区?
分布式系统中,多个节点之间的网络本来是连通的,但是因为某些故障(比如部分节点网络出了问题)某些节点之间不连通了,整个网络就分成了几块区域,这就叫 网络分区。
1.2.修正后的理论(为什么不能同时满足强一致和高可用?)
后来这个说法被更正了,当发生网络分区的时候,如果我们要继续服务,那么强一致性和可用性只能 2 选 1
为啥不能同时满足强一致性和高可用性呢? 举个例子:若系统出现“分区”,系统中的某个节点在进行写操作。为了保证强一致性, 必须要禁止其他节点的读写操作,这就和 高可用性 发生冲突了。如果为了保证 高可用性,其他节点的读写操作正常的话,那就和 强一致性 发生冲突了。
Nacos 不仅支持 CP 也支持 AP。要看你的具体实现。
Eureka 保证的则是 AP。 Eureka 在设计的时候就是优先保证 A (可用性)。在 Eureka 中不存在什么 Leader 节点,每个节点都是一样的、平等的。
2.BASE理论
2.1.核心思想
BASE 是 Basically Available(基本可用)、Soft-state(软状态) 和 Eventually Consistent(最终一致性) 三个短语的缩写。BASE 理论是对 CAP 中一致性 C 和可用性 A 权衡的结果。它大大降低了我们对系统的要求。
BASE 理论的核心思想:
- 基本可用:基本可用是指分布式系统在出现不可预知故障的时候,允许损失部分可用性。譬如:
- 响应时间上的损失: 正常情况下,处理用户请求需要 0.5s 返回结果,但是由于系统出现故障,处理用户请求的时间变为 3 s。
- 系统功能上的损失:正常情况下,用户可以使用系统的全部功能,但是由于系统访问量突然剧增,系统的部分非核心功能无法使用。
- 软状态:即指允许系统中的数据存在中间状态。就是允许系统不同节点的数据副本之间进行数据同步的过程存在延时。
- 最终一致性:系统保证最终数据能够达到一致。
2.2.一致性的级别划分和实现最终一致性的方法
- 强一致性:系统写入了什么,读出来的就是什么。
- 弱一致性:尽量保证某个时刻达到数据一致的状态。
- 最终一致性:弱一致性的升级版,系统保证在某个时刻达到数据一致的状态
业界比较推崇是最终一致性级别。
实现最终一致性的具体方式是什么呢?
- 读时修复 : 在读取数据时,检测数据的不一致,进行修复。
- 写时修复 : 在写入数据,检测数据的不一致时,进行修复(较推荐,这种方式对性能消耗比较低)。
- 异步修复 : 这个是最常用的方式,通过定时对账检测副本数据的一致性,并修复。
3.Raft算法
3.1.节点
在 Raft 算法中,每个服务器节点都有一个状态机,它根据接收到的命令(cmd)来更新自身的状态。
对于一个Raft集群,在任意的时间,集群中的节点一定处于以下三个状态中的一个:
- Leader:负责发起心跳,响应客户端,创建日志,同步日志。
- Candidate:Leader 选举过程中的临时角色,由 Follower 转化而来,发起投票参与竞选。
- Follower:接受 Leader 的心跳和日志同步数据,投票给 Candidate。
在正常的情况下,只有一个服务器是 Leader,剩下的服务器是 Follower。Follower 是被动的,它们不会发送任何请求,只是响应来自 Leader 和 Candidate 的请求。
3.2.term号
raft 算法将时间划分为任意长度的任期(term),任期用连续的数字表示,看作当前 term 号。
每一个任期的开始都是一次选举,在选举开始时,一个或多个 Candidate 会尝试成为 Leader。如果一个 Candidate 赢得了选举,它就会在该任期内担任 Leader。如果没有选出 Leader,将会开启另一个任期,并立刻开始下一次选举。
raft 算法保证在给定的一个任期最少要有一个 Leader。每个节点都会存储当前的 term 号,当服务器之间进行通信时会交换当前的 term 号;
如果有服务器发现自己的 term 号比其他人小,那么他会更新到较大的 term 值。
如果一个 Candidate 或者 Leader 发现自己的 term 过期了,他会立即退回成 Follower。
如果一台服务器收到的请求的 term 号是过期的,那么它会拒绝此次请求。
3.3.日志
- entry:一个事件称为 一个entry,只有 Leader 可以创建 entry。entry 的内容为<term,index,cmd>其中 index是entry在log中的序号。cmd 是可以应用到状态机的操作。
- log:由 entry 构成的数组。只有 Leader 才可以改变其他节点的 log。entry 总是先被 Leader 添加到自己的 log 数组中,然后再发起共识请求,获得同意后才会被 Leader 提交给状态机。Follower 只能从 Leader 获取新日志和当前的 commitIndex,然后把对应的 entry 应用到自己的状态机中。
commitIndex 是一个索引值,它表示在日志(log)中已经被领导者(Leader)确认为安全(即已经达成共识,被大多数服务器节点接受)的条目(entry)的最大索引。简单来说,commitIndex 标记了可以安全地应用到状态机的 entry 的范围。
3.4.选举机制
- 角色状态
- 最初,所有服务器节点都是跟随者。当一个跟随者在一定时间内没有收到领导者的心跳时,它会认为当前系统中没有领导者,于是将自己的角色转换为候选人,开始进行选举。
- 选举过程
- 投票请求:候选人会向其他服务器节点发送投票请求,这个请求包含了候选人的日志信息(如最后一条日志的索引和任期号)。
- 投票规则 :其他服务器节点在收到投票请求后,会根据一定的规则决定是否投票给该候选人。如果候选人的日志足够新,并且该服务器节点还没有投票给其他候选人,就会投票给这个候选人。
- 当选条件 :如果候选人获得了系统中大多数服务器节点的投票,它就当选为新的领导者。例如,在一个有 5 个服务器节点的系统中,候选人需要获得至少 3 票才能当选。
3.5.心跳机制
- 领导者的心跳发送
一旦领导者当选,它会周期性地向其他服务器节点(跟随者)发送心跳消息。其主要作用是告知跟随者当前领导者仍然存活,并且维护领导者的权威。它还用于复制日志等操作,将领导者的日志同步到跟随者。 - 跟随者的心跳接收
- 跟随者在收到心跳消息后,会重置超时计时器。如果在超时时间(electionTimeout)内持续收到心跳消息,就一直保持跟随者状态。
- 如果跟随者在超时时间内没有收到领导者的心跳消息,就会触发新的选举过程,尝试选举出新的领导者。这个超时时间通常是一个随机值,用于避免多个跟随者同时发起选举导致的冲突。
4.API网关
4.1.基础概念和常见功能
API网关(API Gateway)是微服务架构中的核心组件,充当统一入口,负责管理、保护和协调客户端与后端服务之间的通信。
网关常见功能如下:
- 请求转发:将请求转发到目标微服务。
- 负载均衡:当有多个微服务实例时,网关可以将请求均匀地分配到这些实例上。
- 安全认证:对用户请求进行身份验证并仅允许可信客户端访问 API,并且还能够使用类似 RBAC 等方式来授权。
- 响应聚合:某些情况下用户请求要获取的响应内容可能会来自于多个业务服务。网关作为业务服务的调用方,可以把多个服务的响应整合起来,再一并返回给用户。
- 协议转换:例如将客户端的 RESTful HTTP 请求转换为后端服务使用的消息队列协议或者 gRPC 协议。
- 流量控制:对请求的流量进行控制,也就是限制某一时刻内的请求数。
- 熔断降级:在服务不可用时快速失败,避免请求堆积导致雪崩效应。
- 参数校验:支持参数映射与校验逻辑。
- 日志记录:记录所有请求的行为日志供后续使用。
- 监控告警:从业务指标、机器指标、JVM 指标等方面进行监控并提供配套的告警机制。
4.2.其和反向代理的区别?
- 功能复杂度
反向代理功能相对简单,主要关注请求转发和负载均衡。而 API 网关功能更复杂,除了请求转发外,还包括响应聚合、协议转换、限流熔断等多种功能。 - 请求处理方式
反向代理通常只是简单地根据预设规则将请求转发到后端服务器,对请求本身不进行太多处理。API 网关会对请求进行深度处理,例如组合多个请求等等。 - 适用场景
反向代理适用于需要简单地将客户端请求分发到多个后端服务器的场景,如网站的负载均衡和简单的安全防护。API 网关更适合于微服务架构,提供统一的入口和全面的请求处理能力。
4.3.Spring Cloud Gateway
4.3.1.工作流程
客户端的请求先通过匹配规则找到合适的路由,就能映射到具体的服务。然后请求经过过滤器处理后转发给具体的服务,服务处理后,再次经过过滤器处理,最后返回给客户端。
具体的流程分析:
- 路由判断:客户端的请求到达网关后,先经过 Gateway Handler Mapping 处理,这里面会做断言判断,看下符合哪个路由规则,这个路由映射后端的某个服务。
- 请求过滤:然后请求到达 Gateway Web Handler,这里面有很多过滤器,组成过滤器链(Filter Chain),这些过滤器可以对请求进行拦截和修改,比如添加请求头、参数校验等等,有点像净化污水。然后将请求转发到实际的后端服务。这些过滤器逻辑上可以称作 Pre-Filters,Pre 可以理解为“在…之前”。
- 服务处理:后端服务会对请求进行处理。
- 响应过滤:后端处理完结果后,返回给 Gateway 的过滤器再次做处理,逻辑上可以称作 Post-Filters,Post 可以理解为“在…之后”。
- 响应返回:响应经过过滤处理后,返回给客户端。
4.3.2.断言和路由
断言就是一个条件判断,当请求满足这个条件时,就会触发相应的路由规则。(一般是根据请求的路径、方法、头部、参数等属性进行条件判断)
一个路由规则可以包含多个断言。如果一个路由规则中有多个断言,则需要同时满足这些断言才能匹配。
如果一个请求断言后,可以匹配到多个路由,则映射第一个匹配成功的路由。
4.3.3.Spring Cloud Gateway 如何实现动态路由?
因为Spring Cloud Gateway 作为微服务的入口,需要尽量避免重启,所以路由规则不建议写在spring cloud Gateway的配置文件里。所以现在更偏向的是动态路由的方案。
实现动态路由最常见的方案是基于 Nacos 注册中心来做。
4.3.4.过滤器
过滤器 Filter 按照请求和响应可以分为两种
- Pre 类型:在请求被转发到微服务之前,对请求进行拦截和修改,例如参数校验、权限校验、流量监控、日志输出以及协议转换等操作。
- Post 类型:微服务处理完请求后,返回响应给网关,网关可以再次进行处理,例如修改响应内容或响应头、日志输出、流量监控等。
另外一种分类是按照过滤器 Filter 作用的范围进行划分:
- GatewayFilter:局部过滤器,应用在单个路由或一组路由上的过滤器。
- GlobalFilter:全局过滤器,应用在所有路由上的过滤器。
Spring Cloud Gateway 自带了限流过滤器。
4.3.5.如何自定义全局异常处理?
在 SpringBoot 项目中,我们捕获全局异常只需要在项目中配置 @RestControllerAdvice和 @ExceptionHandler就可以了。不过,这种方式在 Spring Cloud Gateway 下不适用。
Spring Cloud Gateway 提供了多种全局处理的方式,比较常用的一种是实现ErrorWebExceptionHandler并重写其中的handle方法。
@Order(-1)
@Component
@RequiredArgsConstructor
public class GlobalErrorWebExceptionHandler implements ErrorWebExceptionHandler {
private final ObjectMapper objectMapper;
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
// ...
}
}