Spring Cloud 技术详解
一、微服务的优缺点及问题
(一)优点
- 松耦合,聚焦单一业务功能。
- 无关开发语言,降低团队规模。
- 开发时专注当前功能,便利集中,功能小而精。
- 一个功能受损对其他功能影响不大,可快速定位问题。
- 不与 html、css 或其他界面混合,专注业务逻辑代码。
- 可灵活搭配技术,独立性强。
(二)缺点
- 服务数量增加,管理复杂,部署复杂,服务器需求增多。
- 服务通信和调用压力增大。
- 运维工程师压力增大,人力资源增多。
- 系统依赖增强。
- 数据一致性和性能监控问题突出。
(三)遇到的问题
- 管理复杂:随着服务增多,管理难度加大。
- 部署复杂:多个服务部署协调困难。
- 通信压力:服务间通信频繁可能导致性能问题。
- 数据一致性难以保证。
- 性能监控困难。
二、Spring Cloud 五大组件
Spring Cloud 是一系列框架的有序集合,利用 Spring Boot 的开发便利性简化分布式系统基础设施开发,可一键启动和部署。
早期的 Spring Cloud 五大组件:
- Eureka:注册中心。
- Ribbon:负载均衡。
- Feign:远程调用。
- Hystrix:服务熔断。
- Zuul/Gateway:网关。
随着 Spring Cloud Alibaba 在国内兴起,项目中可能使用的组件有:
- 注册中心/配置中心 Nacos。
- 负载均衡 Ribbon。
- 服务调用 Feign。
- 服务保护 Sentinel。
- 服务网关 Gateway。
三、Nacos 与 Eureka 的区别
- Nacos 支持服务端主动检测提供者状态:
- 临时实例采用心跳模式。
- 非临时实例采用主动检测模式。
- 临时实例心跳不正常会被剔除,非临时实例则不会。
- Nacos 支持服务列表变更的消息推送模式,服务列表更新更及时。
- Nacos 集群默认采用 AP 方式,当集群中存在非临时实例时,采用 CP 模式。
总结:Eureka 采用 AP 方式,Nacos 默认是 AP 模式,可以采用 CP 模式。
四、Eureka 工作原理
- Eureka Server:服务注册中心(可以是一个集群),对外暴露自己的地址。
- 提供者:启动后向 Eureka 注册自己信息(地址,提供什么服务)。
- 消费者:向 Eureka 订阅服务,Eureka 会将对应服务的所有提供者地址列表发送给消费者,并且定期更新。
- 心跳(续约):提供者定期通过 http 方式向 Eureka 刷新自己的状态(每 30s 定时向 Eureka Server 发起请求)。
五、Feign 工作原理
- 主程序入口添加
@EnableFeignClients
注解开启对 Feign Client 扫描加载处理,定义接口并加@FeignClient
注解。 - 程序启动时进行包扫描,将带有
@FeignClients
注解的类信息注入 Spring IOC 容器。 - 当 Feign 接口中的方法被调用时,通过 JDK 的代理方式生成 RequestTemplate 对象,该对象封装 HTTP 请求需要的全部信息。
- RequestTemplate 生成 Request,交给 Client 处理,Client 可以是 JDK 原生的 URLConnection、Apache 的 HttpClient 或 OKhttp,最后 Client 被封装到 LoadBalanceClient 类,结合 Ribbon 负载均衡发起服务之间的调用。
六、Hystrix 简介
在分布式系统中,服务可能失败导致雪崩,Hystrix 是防雪崩利器,具有服务降级、服务熔断、服务隔离、监控等防止雪崩的技术。Hystrix 有四种防雪崩方式:
- 服务降级:接口调用失败调用本地方法返回空。
- 服务熔断:接口调用失败进入熔断方法返回错误信息。
- 服务隔离:隔离服务之间相互影响。
- 服务监控:记录服务调用的运行指标。
七、断路器/熔断器及状态
项目中使用 Hystrix 实现的断路器默认关闭,开启需在引导类上添加@EnableCircuitBreaker
注解。
断路器状态机有三个状态:
- closed:关闭状态,放行所有请求,统计异常比例和慢请求比例,超过阈值切换到 open 状态(请求错误率超过 5%默认)。
- open:打开状态,服务调用被熔断,拒绝访问被熔断服务的请求,快速失败走降级逻辑,5 秒后(默认值)进入 half-open 状态。
- half-open:半开状态,放行一次请求,根据执行结果切换状态,请求成功切换到 closed 状态,请求失败切换到 open 状态。
八、服务熔断与服务降级
- 熔断机制:应对雪崩效应的微服务链路保护机制,当微服务不可用或响应时间太长时进行服务降级并熔断调用,快速返回错误响应信息,检测到正常后恢复调用链路。在 Spring Cloud 中通过 Hystrix 实现,监控微服务间调用状况,失败调用到一定阈值(5 秒内调用 20 次失败)启动熔断机制。
- 服务降级:从整体负荷考虑,服务熔断后服务器不再被调用,客户端准备本地 fallback 回调返回缺省值。
九、服务雪崩效应
在大型互联网项目中,某个服务宕机时,调用该服务的其他服务也会宕机,微服务之间调用互通,导致服务不可用逐步扩大,使整个项目服务宕机崩溃。
十、限流及配置文件管理
你们项目中有没有做过限流 ? 怎么做的 ?
(一)限流
- 常见限流算法:漏桶算法和令牌桶算法。
- 漏桶算法:类似注水漏水过程,以一定速率流出水,任意速率流入水,超过桶流量则丢弃,保证整体速率恒定。
- 令牌桶算法:存放固定容量令牌的桶,按固定速率添加令牌,桶满时新令牌被丢弃,请求到达时尝试获取令牌,有令牌则处理请求,无令牌则排队等待或直接丢弃,能处理突发流量。
- 漏桶算法:类似注水漏水过程,以一定速率流出水,任意速率流入水,超过桶流量则丢弃,保证整体速率恒定。
从作用上来说,漏桶和令牌桶算法最明显的区别就是是否允许突发流量(burst)的处理,漏桶算法能够强行限制数据的实时传输(处理)速率,对突发流量不做额外处理;而令牌桶算法能够在限制数据的平均传输速率的同时允许某种程度的突发传输。
限流算法区别
2. 参考回答
在我们的项目中,由于流量较大,采用了令牌桶算法进行限流,该限流操作在 gateway 中进行设置。令牌桶就如同一个存放固定容量令牌的容器,按照固定速率 r 不断地往桶里添加令牌。桶中最多可存放 b 个令牌,一旦桶满,新添加的令牌就会被丢弃。当有请求到达时,会尝试从桶中获取令牌。若能获取到令牌,则继续处理请求;若没有令牌可获取,请求则会排队等待或者直接被丢弃。与漏桶算法不同,漏桶算法的流出速率恒定,而令牌桶算法的流出速率有可能大于 r,这意味着对于突发流量,令牌桶算法也能够有效应对。
具体而言,我们在网关路由中进行过滤器配置,以此来设置桶的容量大小以及令牌的固定生成速率。同时,为了更加精准地进行限流控制,我们通常会按照用户访问的 IP 进行限制。由于令牌的存储和管理需要高效且可共享,所以我们将令牌存入 Redis 中,这也使得项目需要集成 Redis 使用。通过这样的方式,我们能够有效地应对项目中的大流量请求,确保系统的稳定运行。
(二)配置文件管理
你们项目的配置文件是怎么管理的 ?
大部分固定配置文件放在服务本地,环境不同可能变化的部分放到 Nacos 中,Nacos 存放各个微服务共享的、需动态变更的配置。
十一、微服务之间独立通讯方式
- 同步通信:Dubbo 通过 RPC 远程过程调用、Spring Cloud 通过 REST 接口 json 调用等。
- 异步通信:消息队列,如 RabbitMQ、ActiveM、Kafka 等。
分布式事务详解
一、分布式事务概述
在分布式系统中,一个业务因跨越不同数据库或不同微服务而包含多个子事务,要求所有子事务同时成功或失败,这就是分布式事务。
例如某电商系统下单操作,需请求订单服务、账户服务、库存服务。订单生成后,分别请求账户服务扣减余额和库存服务扣减库存。若后续操作出现问题导致订单生成失败,但账户余额和库存已扣减成功,这就出现了问题,而分布式事务就是解决此类不一致问题的。
二、产生分布式事务的场景
(一)跨库事务
一个应用的某个功能需操作多个库,不同库存储不同业务数据。
(二)分库分表
当一个库数据量较大或预期未来数据量大时,会进行水平拆分即分库分表。例如将数据库 B 拆分为两个库,对于分库分表情况,开发人员通常使用数据库中间件降低 SQL 操作复杂性。如一条单库语法的 SQL,在分库后需改写为多条 SQL 分别插入不同分库,此时要保证所有分库操作要不都成功,要不都失败,所以数据库中间件面临分布式事务问题。
(三)跨服务事务
一个应用的某个功能需调用多个微服务实现,不同微服务操作不同的数据库。例如 Service A 完成某个功能需直接操作数据库,同时调用 Service B 和 Service C,而 Service B 操作两个数据库,Service C 操作一个数据库,需保证这些跨服务对多个数据库的操作要不都成功,要不都失败,这是典型的分布式事务场景。
三、CAP 理论
CAP 定理由加州大学伯克利分校 Eric Brewer 教授提出,指出 WEB 服务无法同时满足以下三个属性:
(一)一致性(Consistency)
更新操作成功并返回客户端完成后,所有节点在同一时间的数据完全一致(强一致性),不能存在中间状态。
(二)可用性(Availability)
系统提供的服务必须一直处于可用状态,对于用户的每一个操作请求总是能够在有限的时间内返回结果。
(三)分区容错性(Partition tolerance)
分布式系统在遇到任何网络分区故障时,仍然需要能够保证对外提供满足一致性和可用性的服务,除非是整个网络环境都发生了故障。
四、分布式系统为何无法同时保证一致性和可用性
对于分布式系统,各节点之间存在网络交互。网络存在延迟且无法 100%确保可用,所以分区网络故障不可避免。在此条件下,要保证各节点一致性,就必须在一个节点数据变更时将数据同步给另一个节点,而在数据同步过程中,被同步的节点不能对外提供服务,否则会出现数据不一致,这就违背了可用性。所以在存在系统分区的场景下,可用性和一致性无法同时满足。
五、BASE 理论
CAP 是分布式系统设计理论,BASE 是 CAP 理论中 AP 方案的延伸,核心思想是即使无法做到强一致性,但应用可以采用适合的方式达到最终一致性。其思想包含三方面:
(一)Basically Available(基本可用)
分布式系统在出现不可预知的故障时,允许损失部分可用性,但不等于系统不可用。
(二)Soft state(软状态)
允许系统中的数据存在中间状态,认为该中间状态的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时。
(三)Eventually consistent(最终一致性)
强调系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态。其本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。
六、分布式事务的常见解决方案
(一)2PC(两阶段提交)
- 两阶段提交又称 2PC,是一个非常经典的强一致、中心化的原子提交协议。中心化是指协议中有两类节点:中心化协调者节点(coordinator)和 N 个参与者节点(participant)。
- 两个阶段:
- 第一阶段:投票阶段。事务询问协调者向所有的参与者发送事务预处理请求(Prepare),并开始等待各参与者的响应。参与者执行本地事务操作,但不真正提交数据库本地事务,而是先向协调者报告“我这边可以处理了/我这边不能处理”。
- 第二阶段:提交/执行阶段。如果所有参与者反馈给协调者的信息都是 Yes,表示事务可以执行,协调者向所有参与者节点发出 Commit 请求,参与者收到 Commit 请求后正式执行本地事务 Commit 操作,并在完成提交之后释放整个事务执行期间占用的事务资源。
- 第一阶段:投票阶段。事务询问协调者向所有的参与者发送事务预处理请求(Prepare),并开始等待各参与者的响应。参与者执行本地事务操作,但不真正提交数据库本地事务,而是先向协调者报告“我这边可以处理了/我这边不能处理”。
(二)TCC(Try-Confirm-Cancel)
- TCC 又称补偿事务,核心思想是“针对每个操作都要注册一个与其对应的确认和补偿(撤销操作)”。
- 分为三个操作:
- Try 阶段:主要是对业务系统做检测及资源预留。
- Confirm 阶段:确认执行业务操作。
- Cancel 阶段:取消执行业务操作。
- TCC 事务的处理流程与 2PC 两阶段提交类似,不过 2PC 通常在跨库的 DB 层面,而 TCC 本质上是应用层面的 2PC,需通过业务逻辑实现。优势在于可以让应用自己定义数据库操作粒度,降低锁冲突、提高吞吐量。不足之处是对应用的侵入性非常强,业务逻辑的每个分支都需要实现 try、confirm、cancel 三个操作,实现难度大,且 confirm 和 cancel 接口必须实现幂等。
(三)MQ 分布式事务
上面的三种分布式事务的解决方案适用于对数据一致性要求很高的场景。适用于对数据强一致性要求不高但要求数据最终一致的场景。例如在支付系统中,常使用基于 MQ 实现的分布式事务解决方案。以借呗借钱为例,借呗审核通过后同步生成借款单,借款单生成后向 MQ 发送消息通知支付宝转账,支付宝读取 MQ 消息并增加账户余额。最复杂的是保障本地事务和 MQ 消息发送在同一个事务中执行,若中途操作发生异常,如支付宝余额增加发生问题,需人工解决,事故概率极低。
**例题: **
向借呗申请借钱,借呗审核通过后支付宝的余额才会增加,但借呗和支付宝有可能不是同一个系统,这时候如何实现事务呢?实现方案如下图:
执行流程如下:
- 用户向花呗借钱。
- 若花呗借钱审核通过,则同步生成借款单。
- 在借款单生成后,向消息队列(MQ)发送消息,以通知支付宝进行转账操作。
- 支付宝读取 MQ 消息,并增加账户余额。
在上述流程中,最为复杂的部分实际上是如何确保步骤 2 和步骤 3 在同一个事务中执行,也就是实现本地事务和 MQ 消息发送在同一个事务中。只有在借款结束后,借呗的数据处理才算完成,此时支付宝才能读取到消息并执行余额增加操作,从而完成整个流程。
然而,如果在中途操作发生异常,例如支付宝余额增加出现问题,目前并没有特别好的自动解决办法,通常需要人工进行干预解决。虽然这种事故发生的概率极低,但在设计系统时也需要考虑到这种情况的存在,并制定相应的应急预案和人工处理流程,以确保在出现问题时能够及时有效地进行处理,保障系统的稳定性和数据的准确性。
七、Seata 的架构
Seata 事务管理中有三个重要角色:
(一)TC (Transaction Coordinator) -事务协调者
维护全局和分支事务的状态,协调全局事务提交或回滚。
(二)TM (Transaction Manager) -事务管理器
定义全局事务的范围、开始全局事务、提交或回滚全局事务。
(三)RM (Resource Manager) -资源管理器
管理分支事务处理的资源,与 TC 交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
八、XA 模式的工作流程
分为两个阶段:
(一)RM 一阶段的工作
- 注册分支事务到 TC。
- 执行分支业务 SQL 但不提交。
- 报告执行状态到 TC。
(二)TC 二阶段的工作
- TC 检测各分支事务执行状态。
- 如果都成功,通知所有 RM 提交事务。
- 如果有失败,通知所有 RM 回滚事务。
(三)RM 二阶段的工作
接收 TC 指令,提交或回滚事务。
XA 模式牺牲了可用性,保证了强一致性。
九、AT 模型的工作原理
- 阶段一 RM 的工作:
- 注册分支事务。
- 记录 undo-log(数据快照)。
- 执行业务 SQL 并提交。
- 报告事务状态。
- 阶段二提交时 RM 的工作:删除 undo-log 即可。
- 阶段二回滚时 RM 的工作:根据 undo-log 恢复数据到更新前。
AT 模式牺牲了一致性,保证了可用性。
十、TCC 模型的工作原理
TCC 模式与 AT 模式非常相似,每阶段都是独立事务,不同的是 TCC 通过人工编码来实现数据恢复。需要实现三个方法:
(一)Try:资源的检测和预留。
(二)Confirm:完成资源操作业务;要求 Try 成功 Confirm 一定要能成功。
(三)Cancel:预留资源释放,可以理解为 try 的反向操作。
Seata 中的 tcc 模型执行流程如下:
- 阶段一 RM 的工作:
- 注册分支事务。
- 执行 try 操作预留资源。
- 报告事务状态。
- 阶段二提交时 RM 的工作:根据各分支事务的状态执行 confirm 或者 cancel。