当前位置: 首页 > news >正文

微服务面试基础

——————↓↓↓↓↓以下为面试篇↓↓↓↓↓↓——————

面试篇:分布式事务定义、CAP定理、BASE理论、AT模式流程与脏写问题

要清晰讲解分布式事务相关内容,我们可以从分布式事务定义、CAP定理、BASE理论、AT模式流程与脏写问题这几个核心部分逐步展开:

一、分布式事务的定义

分布式事务指:事务操作跨越多个服务或多个数据库的场景,而非单一服务/数据库内的事务。常见场景包括:

  • 跨数据源的分布式事务(操作多个不同数据库);

  • 跨服务的分布式事务(微服务调用中,多个服务的数据库操作需统一事务);

  • 以上两种的综合情况。

二、CAP定理:分布式系统的“三角取舍”

1998年,计算机科学家Eric Brewer提出CAP定理:分布式系统有三个核心指标,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance),但三者无法同时满足。

1. 三个指标的含义
  • 一致性(Consistency):用户访问分布式系统的任意节点,获取的数据必须一致。

比如有两个节点node01node02,初始数据都是data:v0。若node01的数据被改为v1,必须同步到node02,才能保证用户访问任意节点都得到v1

  • 可用性(Availability):用户访问分布式系统时,读、写操作总能成功。

若系统“只能读不能写”“只能写不能读”,甚至两者都不可用,就是“弱可用”或“不可用”。

  • 分区容错性(Partition tolerance

    • Partition(分区):分布式节点间因网络故障无法通信,形成“网络分区”(如node01/node02能通信,但与node03断开,node03成为独立分区)。

    • Tolerance(容错):即使出现网络分区,系统仍要持续对外提供服务。

2. 为什么三者不可兼得?

分布式系统中,网络故障(分区)是必然会发生的(网络无法100%可靠)。因此,分区容错性(P)是分布式系统的“硬性要求”(必须能应对分区,持续服务)。

此时,只能在一致性(C)和可用性(A)中二选一:

  • 选“可用性(A)”:允许用户任意读写,保证服务能访问。但分区存在时,部分节点数据无法同步(如node03无法同步node01的新数据),会出现数据不一致。这种情况满足 AP(Availability + Partition tolerance,牺牲一致性)。

  • 选“一致性(C)”:出现分区时,禁止写操作(只能读),直到网络恢复、分区消失,保证数据一致。但此时写操作不可用,满足 CP(Consistency + Partition tolerance,牺牲可用性)。

三、BASE理论:“退而求其次”的最终一致

CAP定理要求在“C”和“A”之间取舍,但如果牺牲了一致性,数据临时不一致怎么办?BASE理论给出了思路:不追求“强一致性”(任何时刻都一致),而是追求“最终一致性”。

BASE包含三个要点:

  • 基本可用Basically Available:系统故障时,允许损失部分可用性,但核心功能必须可用。

比如电商系统库存服务故障时,“下单”核心流程仍能执行,只是库存显示可能延迟。

  • 软状态(Soft State):允许系统在一定时间内存在中间状态(临时不一致)。

比如数据同步过程中,节点间暂时存在数据差异,但这个“不一致”是临时的。

  • 最终一致性(Eventually Consistent):虽然无法保证“强一致性”,但经过“软状态”后,最终能达到数据一致。

基于BASE理论,解决分布式事务有两个核心思路:

  • AP思想:各子事务分别执行、提交,不锁定数据,允许临时不一致;之后通过“弥补措施”(如回滚、补偿)实现最终一致。Seata的AT模式就是这种思路。

  • CP思想:各子事务执行后不提交,等所有子事务结果;之后“同时提交”或“同时回滚”。过程中锁定资源(不允许其他操作),数据暂时不可用,但保证一致性。XA模式属于这种思路。

四、Seata AT模式:流程与“脏写”问题

Seata的AT模式是AP思想的典型实现,分两个阶段处理分布式事务,同时要解决“极端情况下的脏写问题”。

1. AT模式的两阶段流程

AT模式涉及三个角色:

  • TM(事务管理器):发起并管理全局事务。

  • RM(资源管理器):管理各数据源的分支事务(执行SQL、记录快照等)。

  • TC事务协调器:协调全局事务的提交/回滚,管理全局锁。

(1)第一阶段:记录快照,执行并提交分支事务

流程如下(结合第三张图):

  1. TM开启全局事务

  2. TM调用各分支事务(如跨服务的数据库操作)。

  3. RM执行分支事务的SQL,并提交本地事务(此时本地数据已修改,系统保持“可用”);同时记录数据更新前的快照(undo log)(用于后续回滚)。

  4. RMTC注册分支事务,并报告事务状态(成功/失败)。

(2)第二阶段:根据全局结果,提交或回滚

流程如下(结合第四张图):

  • 若所有分支事务都成功:TC通知各RM,删除第一阶段的undo log(因为本地事务已提交,无需回滚)。

  • 若任意分支事务失败:TC通知各RM,根据undo log回滚数据(恢复到更新前的状态),然后删除undo log。

2. 脏写问题:极端并发下的隐患

大多数情况AT模式没问题,但多线程并发操作同一份数据时,可能出现“脏写”(覆盖合法更新)。

举个例子(结合第五张图):

现有account表,id=1money初始为100,事务1和事务2都要修改它:

  • 事务1流程

    • 获取数据库锁(DB锁),保存数据快照({"id":1, "money":100})。

    • 执行业务SQL:money = money - 10(数据库中money变为90)。

    • 提交本地事务,释放DB锁(此时其他事务可获取DB锁)。

  • 事务2流程

    • 事务1释放DB锁后,事务2获取DB锁,保存快照(此时数据库中money是90,所以快照为{"id":1, "money":90})。

    • 执行业务SQL:money = money - 10(数据库中money变为80)。

    • 提交本地事务,释放DB锁。

  • 事务1的回滚(假设需要回滚)

事务1要根据最初的快照(money:100)回滚数据,但此时数据库中money已被事务2改成80。若强行回滚到100,会覆盖事务2的合法更新,这就是脏写。

3. 脏写的解决:全局锁

为解决脏写,AT模式引入全局锁(由TC管理),规则是:事务在释放DB锁之前,必须先获取全局锁(保证同一时间只有一个事务能操作同一份数据)。

流程如下(结合第六张图):

  • 事务1流程

    • 获取DB锁,保存快照({"id":1, "money":100})。

    • 获取全局锁(TC记录:“事务1正在操作accountid=1的数据”)。

    • 执行业务SQL(money变90)。

    • 提交本地事务,释放DB锁(但全局锁仍由事务1持有)。

    • 若事务1需要回滚,再次获取DB锁,根据快照(100)回滚。

  • 事务2流程

事务1持有全局锁时,事务2尝试获取全局锁(操作同一份数据)会失败。此时事务2会重试(默认30次,间隔10毫秒);若超时仍未获取,事务2会回滚并释放DB锁,不会修改数据。

这样,事务2无法在事务1的全局锁释放前修改数据,避免了“回滚覆盖合法更新”的脏写问题。


在 Seata AT 模式中,全局锁的释放时机与全局事务的最终状态强绑定,而事务 1 的回滚操作只会在全局锁释放之前执行。这一设计从根本上避免了 “事务 1 释放全局锁后,事务 2 修改数据,事务 1 再回滚覆盖” 的问题。

事务 1 释放全局锁的时机(总结:所有分支事务成功通过后,必然要删除undolog日志,必然无法回滚了,所以全局锁也就释放了,此时事务2可以大胆的去操作数据,因为事务1必定不会回滚。)

全局锁由事务协调器(TC)管理,其核心作用是在全局事务未结束前,锁定被操作的数据,防止其他事务修改。事务 1 释放全局锁的时机只有两种:

  1. 全局事务成功提交(第二阶段 “提交”)

  • 若所有分支事务都成功,TM 通知 TC “全局事务提交”。

  • TC 会向所有 RM 发送 “删除 undo log” 的指令(因为分支事务已提交,无需回滚)。

  • RM 删除 undo log 后,向 TC 确认,TC 才会释放事务 1 持有的全局锁

此时,全局事务已彻底结束,事务 1 不会再有任何回滚操作(因为 “提交” 是最终状态)。

  1. 全局事务回滚完成(第二阶段 “回滚”)

  • 若任意分支事务失败,TM 通知 TC “全局事务回滚”。

  • TC 向所有 RM 发送 “回滚” 指令,RM 执行以下操作:

    • 重新获取该数据的数据库锁(DB 锁)(防止此时其他事务修改);

    • 根据第一阶段记录的 undo log,将数据恢复到更新前的状态(比如从 90 回滚到 100);

    • 回滚完成后,删除 undo log,并向 TC 确认;

    • TC 收到所有 RM 的回滚确认后,释放事务 1 持有的全局锁

此时,全局事务已通过回滚恢复一致,事务 1 的回滚操作已完成,后续不会再触发回滚。


总结

从“分布式事务的场景”出发,通过CAP定理理解分布式系统“C/A二选一”的核心矛盾;再通过BASE理论明确“最终一致”的取舍思路;最后聚焦Seata AT模式,了解其“两阶段提交+快照回滚”的流程,以及极端情况下“脏写问题”的成因与“全局锁”的解决方案。整个逻辑围绕“分布式系统如何在一致性与可用性之间取舍,并保证事务最终正确”展开。


面试篇:TCC模式

要清晰讲解 TCC模式,我们可以从核心方法、流程示例、异常问题、优缺点这几个维度逐步展开:

一、TCC的核心:三个手动编码的方法

TCC模式是分布式事务的一种实现,通过人工编码完成“资源预留→确认/回滚”,包含三个关键方法:

  • Try:检测并预留资源。比如“扣钱”场景中,先检查余额是否充足,充足则“冻结”要扣的金额(预留资源,不直接扣减可用余额)。

  • Confirm:完成资源操作的提交。只有所有分支的Try都成功,才会执行Confirm,把“预留的资源”真正提交(比如把冻结的金额扣掉,完成交易)。要求:只要Try成功,Confirm必须能成功(逻辑要足够可靠)。

  • Cancel:释放预留的资源(Try的反向操作)。若任意分支Try失败,执行Cancel,把“预留的资源”释放回去(比如把冻结的金额解冻,恢复可用余额)。

二、TCC流程示例:扣减用户余额

假设账户A初始余额为 100元,需要扣减 30元,分三个阶段分析:

阶段1:Try(资源检测与预留)
  • 操作:检查余额是否≥30元(检测);若充足,执行 冻结金额+30可用金额-30

  • 结果:总金额(冻结+可用)仍为 10030+70),但资源已被“预留”(冻结的30元是后续扣减的准备)。

  • 特点Try阶段的本地事务直接提交,无需等待其他分支事务,能快速释放数据库资源(性能优势)。

阶段2:Confirm(确认提交)
  • 触发条件:所有分支的Try都成功,全局事务需要“提交”。

  • 操作:因为Try已扣减可用金额、增加冻结金额,所以Confirm只需执行 冻结金额-30(把冻结的30元“真正扣掉”)。

  • 结果:冻结金额0,可用金额70,总金额70(扣减完成)。

阶段3:Cancel(释放预留资源)
  • 触发条件:任意分支的Try失败,全局事务需要“回滚”。

  • 操作:执行Try的反向操作——冻结金额-30可用金额+30(把冻结的30元“解冻”,恢复可用余额)。

  • 结果:冻结金额0,可用金额100,总金额回到初始状态(回滚成功)。

三、TCC的异常问题:事务悬挂与空回滚

结合讲义中的“分支阻塞”场景(一个分布式事务包含两个分支,Try阶段一个成功、一个阻塞),分析两类典型问题:

场景背景

分布式事务有两个分支,TM(事务管理器)调用分支时:

  • 第一个分支的Try成功执行;

  • 第二个分支的Try长时间阻塞(如网络延迟、资源锁等待)。

1. 空回滚
  • 触发:第二个分支的Try阻塞时间过长,导致全局事务超时。此时TC(事务协调器)会触发二阶段的Cancel操作,要求两个分支都执行Cancel

  • 问题:第二个分支的Try根本没执行(还在阻塞),但现在要执行它的Cancel。如果直接执行Cancel的“释放预留资源”逻辑,会导致数据错误(因为本来就没预留资源,却要“释放”,相当于“无中生有”地回滚)。

  • 处理:在Cancel方法中,先判断“当前分支是否执行过Try”。如果没执行过Try,则空回滚(不做任何操作),避免数据错误。

2. 事务悬挂
  • 触发:空回滚后,那个“阻塞的Try”最终执行完成了(但此时全局事务已经因为超时而结束,执行了Cancel)。

  • 问题:这个Try执行的是“预留资源”的操作,但全局事务已经结束(不会再执行ConfirmCancel),导致这个分支的事务“只做了一半”,处于悬挂状态(资源被预留,但没人处理后续的提交/回滚)。

  • 处理:在Try方法中,先判断“全局事务是否已经结束(比如是否已执行过Cancel)”。如果全局事务已结束,拒绝执行Try(避免预留资源后无人处理)。

四、TCC的优缺点总结

优点
  1. 性能优秀:一阶段Try执行后直接提交本地事务,释放数据库资源,无需像AT模式那样生成数据快照、持有全局锁,性能比AT更强。

  2. 兼容性广:不依赖数据库事务,靠人工编码的Confirm/Cancel补偿操作实现,因此可用于非事务型数据库(如部分NoSQL数据库)。

缺点
  1. 代码侵入性强:需要手动编写TryConfirmCancel三个接口,开发工作量大,对业务代码侵入严重。

  2. 最终一致性(软状态):事务需经过多阶段,中间是“软状态”(如Try后资源预留但未最终提交),只能保证最终一致性,而非强一致性。

  3. 异常处理复杂:需自行实现幂等性(防止重复操作出错)、事务悬挂、空回滚等问题,开发难度和复杂度高。

通过“核心方法→流程示例→异常问题→优缺点”的逻辑,能清晰理解TCC模式的设计思路与 trade-off(取舍)。


面试篇:Nacos的环境隔离(Namespace)与分级模型(Cluster)

要清晰讲解Nacos的环境隔离(Namespace)与分级模型(Cluster),我们可以从背景需求→功能配置→效果验证→内部逻辑逐步展开:

一、环境隔离:Namespace的作用与实践

企业开发中,存在多环境(开发、测试、生产)或多项目共享Nacos集群的场景。此时需要隔离不同环境/项目的服务注册发现和配置管理,避免互相干扰。

Nacos通过 Namespace 实现环境隔离,隔离层次如图(最外层 Namespace → 中间 Group → 内层 Service/DataId):

1. 创建新的Namespace(开发环境dev
  • Nacos默认有一个public命名空间,所有服务/配置默认归属它。

  • 新建命名空间时,填写表单(如第二张图):

    • 命名空间名:dev(代表“开发环境”)。

    • 描述:开发环境

    • 命名空间ID:不填则自动生成(后续配置需用此ID)。

  • 创建后(第三张图),Nacos会生成唯一ID(如8c468c63-b650-48da-a632-311c75e6d235),且dev命名空间下初始无任何配置(因为之前的配置都在public下)。

2. 微服务指定Namespace

默认微服务注册到public,若要让服务归属dev命名空间,需在配置中指定namespace(用生成的ID)。

item-service为例,修改bootstrap.yml

spring: cloud: nacos: discovery: # 服务发现配置 namespace: 8c468c63-b650-48da-a632-311c75e6d235 # 填写dev命名空间的ID

启动item-service后,查看Nacos服务列表(第五张图):

  • dev命名空间下会出现item-service

  • public命名空间下的其他服务(如user-servicecart-service)仍在原位置——证明不同Namespace的服务相互隔离。

3. 验证Namespace的隔离效果

cart-service(在public命名空间)调用item-service(在dev命名空间)测试:

  • 访问cart-service的Swagger(http://localhost:8082/doc.html),查询购物车列表(第六张图):

结果中商品的newPricenull——因为跨命名空间的服务调用失败。

  • 查看cart-service日志(第七张图):

日志显示No servers available for service: item-service——Nacos在public命名空间找不到item-service的实例,证明Namespace彻底隔离了服务发现。

若将所有服务的namespace统一(如都配置为dev),调用会恢复正常。

二、分级模型:Cluster的作用与实践

大型应用中,服务实例可能分布在不同机房(如上海、杭州机房)。跨机房调用会产生网络延迟,因此需要按“机房”对实例分组管理——Nacos用Cluster(集群)实现这一需求,结构为:服务(Service) → 集群(Cluster) → 实例(Instance)(第八张图)。

1. Nacos内部注册表结构

Nacos内部用多层Map存储服务实例,与分级模型一一对应(第九张图):

  • 最外层:Map<Namespace, Map<Group, Service>> —— 按“命名空间→分组”组织服务。

  • 服务层:Map<String, Cluster> —— 服务下按“集群名”组织集群。

  • 集群层:Set<Instance> —— 集群下存储具体实例(IP、端口等)。

2. 配置服务的集群

默认所有服务的集群为default(第十张图)。若要指定集群(如“北京机房”,集群名BJ),修改bootstrap.yml

spring: cloud: nacos: discovery: cluster-name: BJ # 自定义集群名,代表“北京机房”

修改item-service的配置后,启动新实例(如端口8084),查看Nacos(第十一张图):

  • 新实例(8084)归属BJ集群。

  • 原有实例(80818083)仍在DEFAULT集群——证明同一服务的实例可按集群分组管理。

总结

Nacos通过 Namespace 实现环境/项目级的隔离(服务、配置互不干扰);通过 Cluster 实现机房级的分组(优化跨机房调用,提升性能)。这种“分级模型”让服务治理更灵活,既保障了不同环境的安全性,又能在大规模集群下精细化管理实例。


面试篇:Eureka

要清晰讲解Eureka及其与Nacos的对比,我们可以从Eureka的基础认知、使用方式、与Nacos的核心差异三个维度展开:

一、Eureka是什么?

Eureka是Netflix公司开源的服务注册中心组件,是早期Spring Cloud生态中核心的注册中心方案。它遵循Spring Cloud通用规范,因此和Nacos的使用流程有诸多相似性,但在“服务治理机制”和“功能范围”上存在明显差异。

二、Eureka的Demo与控制台

讲义中的Eureka Demo包含三类组件:

  • eureka-server:Eureka的服务端(注册中心)——注意:Eureka的注册中心需要自行创建项目搭建(不像Nacos是独立中间件,直接部署即可)。

  • order-service:订单服务——作为服务调用者(例如查询订单时,需调用用户服务)。

  • user-service:用户服务——作为服务提供者(对外暴露“查询用户”的接口)。

启动后,访问 localhost:10086 可打开Eureka控制台(如图):

  • 界面展示“系统状态”(Environment、Data center等)、“注册的服务实例列表”(如EUREKASERVERORDERSERVICEUSERSERVICE及其实例状态)。

  • 对比Nacos控制台,Eureka界面更“简陋”,功能展示更简洁。

三、Eureka的使用方式

微服务接入Eureka的流程与Nacos类似,核心两步:

  1. 引入eureka-client依赖。

  2. 配置文件中指定Eureka Server的地址。

  3. 编写服务调用客户端(如OpenFeign)——流程与Nacos几乎一致。

四、Eureka vs Nacos:核心差异

两者都能实现“服务注册发现”,但在服务治理机制和功能范围上有显著区别。

1. 服务注册发现的“及时性”差异(核心!)

服务注册发现的核心是“快速感知服务上下线,保证调用准确性”。Eureka和Nacos的机制设计不同,导致“故障感知速度”有明显差异。

  • 心跳与健康检测周期

    维度

    Eureka

    Nacos

    心跳间隔

    微服务每 30秒 发一次心跳

    微服务每 5秒 发一次心跳

    疑似故障超时

    90秒未收到心跳,认为“疑似故障”

    15秒未收到心跳,认为“疑似故障”

    服务清理周期

    60秒 执行一次清理

    30秒 剔除故障服务;每 5秒 执行检测

      → 结论:Nacos对“服务故障”的感知周期更短、更及时。

    • 服务故障的“容错策略”

      • Eureka为避免“误杀”(如网络抖动导致心跳丢失,但服务实际正常),采取保守策略:

        • 即使长时间没收到心跳,也尽量不轻易剔除服务;

        • 若发现“超过85%的服务心跳异常”,会认为是自身(Eureka Server)网络故障,直接暂停“剔除服务”功能——这会导致“真正故障的服务”长期不被剔除,调用者持续请求故障服务,报错风险高。

      • Nacos策略更“果断”,短周期检测+剔除,能更快隔离故障服务。

    • 服务列表的更新方式

    当服务列表变化时(如服务上线/下线),微服务需拿到最新列表才能正确调用。

    • Eureka:被动拉取——服务列表变更后,Eureka Server不会主动通知微服务,需微服务每30秒主动去拉取最新列表。

      → 微服务感知“列表变化”的延迟很高。

    • Nacos:主动推送 + 定时拉取——

          → 微服务能更快拿到最新列表,调用更准确。

      • 微服务会定时拉取(周期短);

      • Nacos Server在“服务列表变更时”,会主动推送给所有订阅的微服务。

    2. 功能范围的差异
    • Nacos:是“注册中心 + 配置中心”的综合体——不仅能管理服务注册发现,还能统一管理微服务配置(如配置集中存储、动态刷新配置等)。

    • Eureka:只有“注册中心”功能,无“配置管理”能力。若需配置管理,需额外引入其他组件(如Spring Cloud Config)。

    3. 相似点
    • 都支持服务注册与发现(微服务可注册自身、发现其他服务)。

    • 都有基于心跳的健康监测(通过心跳判断服务是否存活)。

    • 都支持集群部署,且集群间数据同步默认采用AP模式(优先保证“可用性(Availability)”,允许临时数据不一致,符合分布式系统“分区容错性(Partition tolerance)”要求)。

    总结

    Eureka是早期Spring Cloud的经典注册中心,使用简单,但在“服务故障感知的及时性”和“功能丰富度”上不如Nacos:

    • 故障感知:Eureka更“迟钝”,Nacos更“敏锐”;

    • 功能范围:Nacos兼具“注册中心+配置中心”,更全能;

    • 场景:当下微服务生态中,Nacos因更及时的治理机制和丰富功能,使用更广泛;Eureka多见于早期Spring Cloud项目。


    面试篇:OpenFeign结合Spring Cloud LoadBalancer实现负载均衡

    这张流程图展示了 OpenFeign结合Spring Cloud LoadBalancer实现负载均衡 的完整流程,可按“请求发起→负载均衡选实例→URI重构→发送真实请求”的顺序分层讲解:

    一、核心组件与分层职责

    流程涉及4类核心组件,各层分工明确:

    • FeignBlockingLoadBalancerClient:OpenFeign的负载均衡客户端,是远程调用的“总协调者”。

    • BlockingLoadBalancerClient:Spring Cloud LoadBalancer提供的负载均衡客户端,负责“选择负载均衡器”。

    • ReactiveLoadBalancer(如RoundRobinLoadBalancer):负载均衡器,负责“执行具体的负载均衡算法”。

    • ServiceInstanceListSupplier + 注册中心(Nacos):负责“从注册中心拉取服务实例列表”。

    二、流程分步解析(必看!!!)

    以“调用item-service/items/10接口”为例,流程如下:

    (传来初始请求--总调用--选择负载均衡器--具体负载均衡器执行--查看注册中心调用选择后的服务实例--将可用服务实例IP+端口号 替换原初始请求--放行)

    1. 步骤1:发起原始请求(FeignBlockingLoadBalancerClient层)
    • 初始请求:http://item-service/items/10(其中item-service是服务名,而非具体IP)。

    • FeignBlockingLoadBalancerClient接收请求后,首先从请求中提取serviceId(即item-service)。

    2. 步骤2:负载均衡选实例(BlockingLoadBalancerClient层)

    FeignBlockingLoadBalancerClient调用 LoadBalancerClient#choose() 方法,进入 BlockingLoadBalancerClient 的逻辑:

    • 获取负载均衡:根据serviceIditem-service),获取对应的 ReactiveLoadBalancer(默认是轮询算法的RoundRobinLoadBalancer)。

    • 调用负载均衡算法:调用 ReactiveLoadBalancer#choose(),进入具体负载均衡器的逻辑。

    3. 步骤3:拉取实例并轮询选择(RoundRobinLoadBalancer层)

    RoundRobinLoadBalancer 执行以下操作:

    • 拉取实例列表:通过 ServiceInstanceListSupplier#get(),从注册中心(Nacos)拉取item-service的所有实例。假设Nacos中item-service注册了两个实例:localhost:8081localhost:8083

    • 轮询选实例:调用 getInstanceResponse(),用轮询算法(“按顺序依次选择”)从实例列表中选一个。假设本次选中 localhost:8081

    4. 步骤4:URI重构与发送请求(FeignBlockingLoadBalancerClient层)
    • URI重构:将原始请求的服务名(item-service)替换为选中实例的IP+端口,得到新URI:http://localhost:8081/items/10。(初始请求:http://item-service/items/10

    • 发送真实请求:向重构后的URI发起HTTP请求,完成远程调用。

    三、设计亮点:“解耦与可扩展”

    整个流程的核心设计思路是“职责分层、组件解耦”:

    • OpenFeign只负责“远程调用的模板化”(封装请求、解析响应),不关心“负载均衡怎么选实例”。

    • Spring Cloud LoadBalancer定义“负载均衡的接口规范”(ReactiveLoadBalancer),并提供默认实现(轮询、随机等),方便替换算法(比如想改成“权重负载均衡”,只需自定义ReactiveLoadBalancer实现类)。

    • 注册中心(Nacos)只负责“存储服务实例的元数据”,与负载均衡逻辑解耦。

    这种设计让系统扩展性极强,各组件可独立升级、替换。


    面试篇:负载均衡流程

    要结合核心代码片段讲解OpenFeign + Spring Cloud LoadBalancer的负载均衡流程,我们可以按“请求入口→负载均衡选实例→拉取实例→轮询算法→URI重构”的顺序,逐段分析关键代码与流程的对应关系:

    一、请求入口:FeignBlockingLoadBalancerClient#execute(OpenFeign触发负载均衡的起点)(必看!!!)

    当通过OpenFeign的客户端(如ItemClient)发起远程调用时,请求会先进入FeignBlockingLoadBalancerClientexecute方法。这段代码是“负载均衡+远程调用”的总入口:

    // FeignBlockingLoadBalancerClient.java @Override public Response execute(Request request, Request.Options options) throws IOException { // 1. 从请求URL中提取服务名(serviceId) URI originalUri = URI.create(request.url()); String serviceId = originalUri.getHost(); Assert.state(serviceId != null, "Request URI需包含有效服务名"); // 2. 负载均衡:选择一个服务实例 ServiceInstance instance = loadBalancerClient.choose(serviceId, buildRequest()); if (instance == null) { throw new IllegalStateException("没有可用实例:" + serviceId); } // 3. 重构URI:把服务名(item-service)替换为实例的IP+端口 URI reconstructedUri = loadBalancerClient.reconstructURI(instance, originalUri); Request newRequest = buildRequest(request, reconstructedUri); // 4. 发起真实请求 return delegate.execute(newRequest, options); }

    代码作用

    • 提取serviceId(如item-service);

    • 调用loadBalancerClient.choose触发负载均衡选实例;

    • 重构URI(如http://item-service/items/10http://localhost:8081/items/10);

    • 发起最终的HTTP请求。

    二、负载均衡选实例:BlockingLoadBalancerClient#choose(选择负载均衡器)

    loadBalancerClient.choose方法实际调用BlockingLoadBalancerClientchoose,负责获取“负载均衡器”并执行选择逻辑:

    // BlockingLoadBalancerClient.java @Override public <T> ServiceInstance choose(String serviceId, Request<T> request) { // 1. 根据serviceId获取对应的负载均衡器(默认是RoundRobinLoadBalancer) ReactiveLoadBalancer<ServiceInstance> loadBalancer = loadBalancerClientFactory.getInstance(serviceId); if (loadBalancer == null) { return null; } // 2. 调用负载均衡器的choose方法,选实例(响应式编程,block转为同步) Response<ServiceInstance> loadBalancerResponse = Mono.from(loadBalancer.choose(request)).block(); return loadBalancerResponse != null ? loadBalancerResponse.getServer() : null; }

    代码作用

    • 为每个服务(如item-service)分配专属的ReactiveLoadBalancer(负载均衡器接口,默认实现是轮询算法的RoundRobinLoadBalancer);

    • 调用负载均衡器的choose方法,执行“选实例”逻辑。

    三、轮询选实例:RoundRobinLoadBalancer#choose(实现轮询算法)

    RoundRobinLoadBalancer是Spring Cloud LoadBalancer提供的默认轮询负载均衡器,核心逻辑在choosegetInstanceResponse中:

    // RoundRobinLoadBalancer.java @Override public Mono<Response<ServiceInstance>> choose(Request request) { // 1. 获取“服务实例列表提供者”(从注册中心拉取实例) ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider .getInstance(response -> new RequestDataContext(new RequestData(request), response)); // 2. 从注册中心拉取实例列表,然后处理选实例逻辑 return supplier.get(request) .next() .map(serviceInstances -> processInstanceResponse(supplier, serviceInstances)); } private Response<ServiceInstance> processInstanceResponse( ServiceInstanceListSupplier supplier, List<ServiceInstance> serviceInstances) { return getInstanceResponse(serviceInstances); } private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) { if (instances.isEmpty()) { return new EmptyResponse(); } // 3. 轮询核心:原子递增位置,取模得到实例索引 int pos = this.position.incrementAndGet() & Integer.MAX_VALUE; ServiceInstance instance = instances.get(pos % instances.size()); return new DefaultResponse(instance); }

    代码作用

    • supplier.get(request):通过ServiceInstanceListSupplier从注册中心(如Nacos)拉取服务实例列表(如item-servicelocalhost:8081localhost:8083);

    • pos % instances.size():轮询算法核心——用原子类position递增计数,对实例数量取模,保证“按顺序依次选择实例”,实现负载均衡。

    四、拉取实例:DiscoveryClientServiceInstanceListSupplier#get(从注册中心拉取实例)

    ServiceInstanceListSupplier的实现类(如DiscoveryClientServiceInstanceListSupplier)负责从注册中心拉取实例列表。若使用Nacos,底层是NacosDiscoveryClient

    // DiscoveryClientServiceInstanceListSupplier.java @Override public Flux<List<ServiceInstance>> get(Request request) { return Flux.defer(() -> { // 从注册中心拉取指定serviceId的所有实例 List<ServiceInstance> instances = discoveryClient.getInstances(serviceId); return Flux.just(instances); }); }

    代码作用

    • discoveryClient.getInstances(serviceId):调用注册中心的客户端(如NacosDiscoveryClient),从Nacos服务器拉取item-service的所有实例元数据(IP、端口等)。

    五、URI重构与真实请求:回到FeignBlockingLoadBalancerClient

    RoundRobinLoadBalancer选出实例(如localhost:8081)后,流程回到FeignBlockingLoadBalancerClient

    // FeignBlockingLoadBalancerClient.java(续) URI reconstructedUri = loadBalancerClient.reconstructURI(instance, originalUri); // 例如:将http://item-service/items/10 → http://localhost:8081/items/10 Request newRequest = buildRequest(request, reconstructedUri); return delegate.execute(newRequest, options); // 发起真实HTTP请求

    代码作用

    • 把“服务名形式的URL”替换为“实例IP+端口的URL”;

    • 通过delegate.execute发起最终的HTTP请求,完成远程调用。

    总结:代码与流程的对应关系

    从“OpenFeign发起请求”到“最终调用成功”,核心代码与流程的对应逻辑是:

    1. FeignBlockingLoadBalancerClient#execute:触发负载均衡,提取serviceId

    2. BlockingLoadBalancerClient#choose:为服务分配负载均衡器;

    3. RoundRobinLoadBalancer#choose:拉取实例+轮询选实例;

    4. DiscoveryClientServiceInstanceListSupplier#get:从注册中心拉取实例;

    5. 重构URI并发起真实请求。

    这种“分层解耦”的设计,让“远程调用”“负载均衡算法”“注册中心交互”职责分离,既保证了流程清晰,又具备极强的扩展性(比如替换负载均衡算法、切换注册中心都很方便)。


    面试篇:NacosLoadBalancer的负载均衡策略(集群优先+权重随机)

    要清晰讲解 NacosLoadBalancer的负载均衡策略(集群优先+权重随机),我们结合代码、源码逻辑、Nacos控制台操作分三步展开:

    一、切换负载均衡策略:从默认轮询到NacosLoadBalancer

    Spring Cloud LoadBalancer默认使用 RoundRobinLoadBalancer(轮询算法),但Nacos提供了更适合生产的 NacosLoadBalancer(支持集群优先+权重随机)。要切换策略,需自定义配置类并指定生效范围。

    1. 自定义负载均衡配置类

    编写OpenFeignConfig类,创建NacosLoadBalancer的Bean(替换默认的ReactorLoadBalancer):

    package com.hmall.cart.config; import com.alibaba.cloud.nacos.NacosDiscoveryProperties; import com.alibaba.cloud.nacos.loadbalancer.NacosLoadBalancer; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer; import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier; import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory; import org.springframework.context.annotation.Bean; import org.springframework.core.env.Environment; public class OpenFeignConfig { @Bean public ReactorLoadBalancer<ServiceInstance> reactorServiceInstanceLoadBalancer( Environment environment, NacosDiscoveryProperties properties, LoadBalancerClientFactory loadBalancerClientFactory) { // 获取服务名(如item-service) String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); // 创建NacosLoadBalancer,传入服务实例列表提供者、服务名、Nacos配置 return new NacosLoadBalancer( loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name, properties); } }

    ⚠️ 注意:这个类不能加@Configuration注解(否则会全局强制覆盖,我们要通过@LoadBalancerClients灵活指定生效范围)。

    2. 配置生效范围(全局/局部)

    在微服务启动类上,用@LoadBalancerClients指定配置生效:

    • 全局生效:对所有远程调用的服务生效

      @LoadBalancerClients(defaultConfiguration = OpenFeignConfig.class) @EnableFeignClients(clients = {ItemClient.class}) @SpringBootApplication public class CartApplication { public static void main(String[] args) { SpringApplication.run(CartApplication.class, args); } }

      • 局部生效:只对特定服务(如item-service)生效

        @LoadBalancerClients({ @LoadBalancerClient(value = "item-service", configuration = OpenFeignConfig.class) })

        3. 验证策略切换

        重启服务后,通过Debug模式查看loadBalancer的类型(如图):

        • 原本默认是RoundRobinLoadBalancer,现在变成NacosLoadBalancer,证明策略切换成功。

        二、NacosLoadBalancer的“集群优先”逻辑

        NacosLoadBalancer的核心优势是优先选择“同集群”的服务实例,减少跨机房/跨集群调用的网络延迟。

        1. 集群的作用与配置

        在分布式系统中,服务可能部署在不同机房/集群(如“杭州集群”“北京集群”)。跨集群调用会因网络距离产生额外延迟,因此需优先调用“同集群实例”。

        微服务的集群由spring.cloud.nacos.discovery.cluster-name配置(如cart-service配置为DEFAULT集群)。

        2. 源码中“集群筛选”逻辑

        查看NacosLoadBalancer的源码(如图中代码片段),核心流程:

        private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> serviceInstances) { // 1. 获取当前服务(调用方)的集群名(如cart-service的cluster-name: DEFAULT) String clusterName = this.nacosDiscoveryProperties.getClusterName(); List<ServiceInstance> instancesToChoose = serviceInstances; if (StringUtils.isNotBlank(clusterName)) { // 2. 筛选:从目标服务(如item-service)的所有实例中,找出“集群名匹配”的实例 List<ServiceInstance> sameClusterInstances = serviceInstances.stream() .filter(instance -> { String instanceCluster = instance.getMetadata().get("nacos.cluster"); return StringUtils.equals(instanceCluster, clusterName); }) .collect(Collectors.toList()); // 3. 若有同集群实例,优先在这些实例中选 if (!CollectionUtils.isEmpty(sameClusterInstances)) { instancesToChoose = sameClusterInstances; } } // 后续在筛选后的实例中做负载均衡... }

        3. 效果验证

        假设:

        • cart-service的集群是DEFAULT

        • item-service有3个实例:2个在DEFAULT集群(8081、8083),1个在BJ集群(8084)。

        当cart-service调用item-service时,NacosLoadBalancer会先筛选出DEFAULT集群的2个实例(如图中sameClusterInstances size=2),优先在这2个实例中做负载均衡,避免跨集群调用。

        三、NacosLoadBalancer的“权重随机”算法

        同集群内的实例,NacosLoadBalancer采用加权随机算法:实例的“权重”越高,被选中的概率越大(可在Nacos控制台手动配置权重)。

        1. Nacos控制台配置权重

        进入Nacos控制台→服务详情→实例“编辑”(如图):

        • 找到目标实例(如item-service的8083实例),修改“权重”(比如设为5,默认是1)。

        2. 加权随机的源码逻辑

        NacosLoadBalancer中,通过getHostByRandomWeight3方法实现加权随机(方法名暗示“加权随机”)。核心逻辑是:

        • 为每个实例生成“权重占比”的区间;

        • 生成随机数,落在哪个实例的区间,就选哪个实例;

        • 权重大的实例,区间范围更大,被选中的概率更高。

        3. 效果验证

        将item-service的8083实例权重设为5,8081实例权重保持1

        • 多次调用购物车接口(触发cart-service调用item-service),会发现8083实例被调用的次数远多于8081(因为权重更高,被选中概率大)。

        总结:NacosLoadBalancer的优势

        相比Spring Cloud LoadBalancer默认的“轮询/随机”,NacosLoadBalancer做到了:

        1. 集群优先:减少跨集群调用的网络延迟,提升性能;

        2. 权重可调:通过Nacos控制台灵活配置实例权重,实现“性能好的实例承担更多流量”,优化资源利用。

        这种策略更贴合生产环境中“多机房部署+差异化实例性能”的复杂场景。


        面试篇:服务保护(以Sentinel为核心)

        要清晰讲解服务保护(以Sentinel为核心)的技术原理,我们可以从线程隔离和四大限流算法(固定窗口、滑动窗口、令牌桶、漏桶)展开,结合类比和示例让逻辑更易懂:

        一、线程隔离:限制下游服务的并发风险

        线程隔离是为了避免“单个下游服务故障”拖垮整个当前服务,有两种实现思路:

        1. 线程池隔离(Hystrix默认方案)
        • 原理:给每个下游服务的调用分配独立线程池。比如调用“服务A”用“服务A线程池”,调用“服务B”用“服务B线程池”。

        • 类比:把每个下游服务的调用比作“不同施工队”,各队有自己的“工人(线程)”。若“服务A”的工人忙到瘫痪,“服务B”的工人仍能正常干活。

        • 优缺点(结合图片):

          • 优点:支持主动超时(线程池可设置超时,到点强制终止请求)、支持异步调用。

          • 缺点:线程有额外开销(创建、销毁、上下文切换耗资源),适合低扇出(调用下游服务少)场景。

        2. 信号量隔离(Sentinel采用的方案)
        • 原理:不用线程池,而是用计数器记录当前调用下游服务的线程数。比如给“服务C”设信号量为10,当10个线程都在调用“服务C”时,新请求直接拒绝。

        • 类比:给每个下游服务发“通行卡”,卡数量固定。卡发完后,新请求要么等卡,要么被拒。

        • 优缺点(结合图片):

          • 优点:轻量级,无额外线程开销(不用建线程池),适合高频调用、高扇出(调用下游服务多)场景。

          • 缺点:不支持主动超时(只能等下游自己响应)、不支持异步调用。

        二、限流算法:控制流量洪峰,保护服务稳定

        限流的核心是限制“单位时间内的请求数(QPS)”,常见4种算法各有特点:

        1. 固定窗口计数(基础但有缺陷)
        • 原理:把时间切成固定长度的窗口(如1秒1个窗口),每个窗口内统计请求数。若请求数超过“限流阈值”,拒绝新请求。

        • 示例(结合图片):

          • 窗口1秒,阈值3。第1、2秒请求数<3,正常;第3秒来5个请求,前3个放行,后2个拒绝。

        • 缺陷:临界漏洞。比如“4.5~5秒”来3个请求,“5~5.5秒”又来3个请求——从“4.5~5.5秒”看,总请求6,远超阈值3,但固定窗口只看“第5秒”和“第6秒”各自的计数,都会放行,导致实际流量超限。

        2. 滑动窗口计数(解决固定窗口的临界问题)
        • 原理:窗口长度固定(如1秒),但窗口是“滑动”的,且内部细分“小区间”(如把1秒分成2个500ms小区间)。统计时,取“当前时间往前推1秒”的所有小区间请求总和,判断是否超阈值。

        • 示例(结合图片,以1秒窗口、2个500ms小区间、阈值3为例):

          • 1300ms请求:当前时间往前推1秒是300ms,找到“500~1000ms”和“1000~1500ms”小区间,统计总和(假设3个),放行。

          • 1400ms请求:两个小区间总和变4,超阈值3,拒绝。

          • 1600ms请求:窗口滑动到“1000~1500ms”和“1500~2000ms”小区间,继续统计。

        • 优势:通过“滑动+细分小区间”,更精确统计“任意1秒内”的请求数,避免固定窗口的临界漏洞。

        3. 令牌桶算法(Sentinel热点参数限流的基础)
        • 原理(结合图片):

          • 系统以固定速率生成“令牌”,存入“令牌桶”(桶有最大容量,满则停止生成)。

          • 请求必须先拿令牌,拿到才会被处理;拿不到则等待或被拒。

        • 特点:允许突发流量。若桶里存了大量令牌(如前几秒无请求,令牌攒着),某刻突发大流量时,能一次性用掉存的令牌,瞬间QPS会超“生成速率”,但长期QPS仍受控。

        • 注意:令牌桶“最大容量”别设为服务QPS上限,要留缓冲,否则突发时仍可能超限。

        4. 漏桶算法(Sentinel排队等待的基础)
        • 原理(结合图片,把请求比作“水滴”,漏桶比作“队列”):

          • 请求到达后,先放入“漏桶”(队列)。

          • 漏桶以固定速率“漏水”(处理请求),桶满则新请求直接丢弃。

        • 特点:流量整型。不管上游请求多“陡”(突发大流量),下游收到的请求都是“平滑的”(固定速率,像大坝拦洪水后匀速泄洪,避免下游被冲垮。

        三、总结:技术选型与场景匹配

        • 线程隔离:高频/高扇出场景选信号量隔离(Sentinel);需超时/异步场景选线程池隔离(Hystrix)。

        • 限流算法

          • 固定窗口:简单但有漏洞,少用;

          • 滑动窗口:精确统计,Sentinel普通限流常用;

          • 令牌桶:支持突发流量,Sentinel热点参数限流用;

          • 漏桶:流量整型,Sentinel排队等待用。

        这些技术共同构成Sentinel的“服务保护”能力,让微服务在流量波动时,既抗住突发,又不因局部故障雪崩。


        面试题

        1. SpringCloud有哪些常用组件?分别是什么作用?

        SpringCloud是微服务开发的“工具集”,常用组件及作用:

        • 服务注册发现:如Nacos、Eureka,负责管理服务的IP、端口等信息,让服务能“找到”彼此。

        • 远程调用:如OpenFeign,基于接口的声明式调用,简化服务间HTTP请求(不用手动写HttpClient)。

        • 负载均衡:如Spring Cloud LoadBalancer(替代Ribbon),帮服务消费者从多个实例中选一个调用,分摊压力。

        • 服务保护:如Sentinel,通过限流、熔断、隔离防止单个服务故障拖垮整个系统。

        • 网关:如Spring Cloud Gateway,统一入口,处理路由、鉴权、限流等(像小区门卫,所有请求先经过它)。

        • 配置中心:如Nacos、Config,集中管理多环境配置,支持动态刷新(改配置不用重启服务)。

        • 分布式事务:如Seata,解决跨服务操作的数据一致性问题(比如下单时扣库存和减余额要同时成功/失败)。

        2. 服务注册发现的基本流程是怎样的?

        核心是“服务自报家门,消费者按需查找”:

        1. 注册:服务启动时,把自己的IP、端口、服务名等信息“上报”给注册中心(如Nacos),注册中心存起来。

        2. 心跳:服务定期给注册中心发“心跳”(比如Nacos默认5秒一次),证明自己还活着;注册中心没收到心跳会把服务标记为下线。

        3. 发现:服务消费者从注册中心“拉取”目标服务的所有实例列表(比如订单服务找用户服务)。

        4. 调用:消费者用负载均衡算法(如轮询)从列表中选一个实例,直接调用。

        5. 更新:注册中心若检测到服务上下线,会主动推送新列表给消费者(如Nacos),或消费者定期拉取(如Eureka)。

        3. Eureka和Nacos有哪些区别?

        两者都是注册中心,但功能和细节差异明显:

        • 功能范围:Nacos是“注册中心+配置中心”二合一;Eureka只有注册中心功能。

        • 健康检测

          • 心跳间隔:Eureka默认30秒,Nacos默认5秒(Nacos更敏感)。

          • 故障剔除:Eureka90秒未收到心跳才标记故障,60秒清理一次;Nacos15秒超时,30秒剔除(Nacos更快)。

        • 服务列表更新:Nacos支持“主动推送+定期拉取”(服务变了马上通知消费者);Eureka只能消费者30秒自己拉取(延迟高)。

        • 容错策略:Eureka更保守(若85%服务心跳异常,会认为自己网络有问题,暂停剔除服务,避免误杀);Nacos更果断。

        4. Nacos的分级存储模型是什么意思?

        Nacos用“多层目录”管理服务实例,类似“文件夹分类”,从外到内:

        • Namespace:环境隔离(如dev、test、prod环境),不同Namespace的服务完全隔离。

        • Group:同一环境内的服务分组(如按业务线分“支付组”“商品组”),默认DEFAULT_GROUP。

        • Service:服务名(如item-service、user-service),是服务的唯一标识。

        • Cluster:集群(如“上海机房”“北京机房”),同一服务的实例按机房分组,减少跨机房调用延迟。

        • Instance:具体服务实例(如192.168.1.1:8081),包含IP、端口等信息。

        这种分级让服务管理更灵活,比如多环境隔离、按机房调度。

        5. OpenFeign是如何实现负载均衡的?

        OpenFeign本身不做负载均衡,而是“搭便车”整合了Spring Cloud LoadBalancer,流程如下:

        1. 代理对象:OpenFeign为接口生成代理对象,当调用接口方法时,代理对象拦截请求。

        2. 提取服务名:从请求URL中拿到服务名(如http://item-service/xxx中的item-service)。

        3. 选实例:通过LoadBalancerClient,根据服务名找对应的负载均衡器(如RoundRobinLoadBalancer轮询、NacosLoadBalancer集群优先+权重),从注册中心拉取的实例列表中选一个。

        4. 重构URL:把服务名替换成选中实例的IP+端口(如http://192.168.1.1:8081/xxx)。

        5. 发请求:用重构后的URL发起HTTP请求,完成远程调用。

        6. 什么是服务雪崩,常见的解决方案有哪些?

        服务雪崩:一个服务故障(如响应慢、宕机),导致依赖它的服务排队等待,资源耗尽,进而引发更多服务故障,像多米诺骨牌倒塌。

        解决方案:

        • 熔断:当服务调用失败率过高(如50%),暂时“断开”调用,直接返回兜底数据(如Sentinel的熔断规则),避免持续浪费资源。

        • 限流:限制单位时间内的请求数(如每秒100次),超出的请求拒绝,防止流量洪峰压垮服务。

        • 隔离:给每个下游服务的调用分配独立资源(如线程池隔离、信号量隔离),一个服务故障不影响其他服务。

        • 降级:非核心功能降级(如商品详情页不显示推荐列表),优先保证核心功能可用。

        • 超时控制:设置调用超时时间(如2秒),超时直接返回,避免无限等待。

        7. Hystrix和Sentinel有什么区别和联系?

        联系:都是服务保护组件,能实现熔断、限流、隔离,防止服务雪崩。

        区别

        • 活跃度:Hystrix已停更,Sentinel是阿里开源,持续维护,功能更丰富。

        • 隔离方式:Hystrix默认用“线程池隔离”(每个服务调用用独立线程池,开销大);Sentinel用“信号量隔离”(计数器控制并发,轻量高效)。

        • 监控与配置:Sentinel有可视化控制台,支持动态配置规则(限流、熔断等);Hystrix配置较繁琐,监控较弱。

        • 适用场景:Sentinel更适合高并发、规则动态变更的场景;Hystrix多见于老项目。

        8. 限流的常见算法有哪些?

        限流是“控制单位时间内的请求数”,常见算法:

        • 固定窗口:把时间切成固定窗口(如1秒),统计窗口内请求数,超阈值则拒绝。缺点:临界时间可能超限(如4.5~5.5秒内请求超阈值,但两个窗口各自没超)。

        • 滑动窗口:将固定窗口分成多个小区间(如1秒分2个500ms区间),窗口随时间滑动,统计“当前时间往前推1秒”的所有区间请求总和。解决固定窗口的临界问题,Sentinel普通限流用这个。

        • 令牌桶:按固定速率生成令牌(如每秒100个),存到令牌桶(有最大容量);请求必须拿令牌才能处理。支持突发流量(桶里攒的令牌可一次性用),Sentinel热点参数限流用这个。

        • 漏桶:请求先进入“漏桶”(队列),桶以固定速率“漏水”(处理请求),满了则丢弃。能让流出的流量更平滑,Sentinel的排队等待功能用这个。

        9. 什么是CAP理论和BASE思想?

        CAP理论:分布式系统有三个指标,不可能同时满足:

        • 一致性(C):所有节点数据一致(如改了A节点,B节点马上同步)。

        • 可用性(A):任何时候请求都能成功(读/写不失败)。

        • 分区容错性(P):网络故障(分区)时,系统仍能工作。

        分布式系统必须满足P(网络故障不可避免),所以只能二选一:CP(保一致,牺牲可用性)或AP(保可用,牺牲强一致)。

        BASE思想:对CAP的妥协,不追求强一致,而是“最终一致”:

        • 基本可用BA:故障时允许部分功能不可用(如降级),但核心功能可用。

        • 软状态(S):允许短时间数据不一致(如同步延迟)。

        • 最终一致性(E):过一段时间后,数据一定一致(如分布式事务的最终一致)。

        10. 项目中碰到过分布式事务问题吗?怎么解决的?

        碰到过,比如“下单流程”:扣库存(库存服务)和扣余额(用户服务)必须同时成功或失败,否则会出现“扣了库存但没扣钱”的问题。

        项目中用Seata的AT模式解决:

        • 引入Seata,每个服务作为分支事务,由TC(事务协调器)协调全局事务。

        • 第一阶段:每个分支事务执行并提交,同时记录数据快照(undo log),用于回滚。

        • 第二阶段:若所有分支成功,TC通知删除快照;若有失败,TC通知用快照回滚,保证最终数据一致。

        11. AT模式如何解决脏读和脏写问题的?

        • 脏写:多个事务同时修改同一数据,后提交的覆盖先提交的。AT模式通过全局锁解决:事务释放数据库锁前,必须先拿全局锁,其他事务没拿到全局锁不能修改,避免并发覆盖。

        • 脏读:一个事务读取到另一个未提交的事务数据。AT模式第一阶段会记录数据快照(更新前的状态),若其他事务修改了数据,回滚时会基于快照恢复,避免读取到中间不一致的数据。

        12. TCC模式与AT模式对比,有哪些优缺点?

        TCC模式(手动编码补偿)(快,不用等其他事务):

        • 优点:

          • 性能好:一阶段直接提交事务,释放资源,不用等其他服务。

          • 兼容性强:不依赖数据库事务,支持非关系型数据库(如MongoDB)。

        • 缺点:

          • 代码侵入性强:需手动写try(资源预留)、confirm(提交)、cancel(回滚)方法,开发量大。

          • 复杂:要处理空回滚(没执行try却执行cancel)、事务悬挂(try执行时全局事务已结束)等问题。

        AT模式(自动快照回滚):

        • 优点:

          • 无侵入:业务代码不用改,框架自动生成快照和回滚逻辑。

          • 简单:开发者不用关心补偿细节。

        • 缺点:

          • 依赖数据库事务:只支持关系型数据库(如MySQL)。

          • 有开销:生成快照、全局锁会消耗资源,性能略低。

        13. RabbitMQ是如何确保消息的可靠性的?

        从“生产→存储→消费”全链路保障: (确认--持久化--确认)

        • 生产端:用“publisher confirm”机制,消息发出去后,RabbitMQ会回传确认(ack),没收到ack则重试;若消息路由失败,用“publisher return”机制退回,避免消息丢失。

        • 存储端:开启持久化(交换机、队列、消息都设为持久化),RabbitMQ重启后消息不丢失。

        • 消费端:关闭自动ACK,处理完消息后手动发送ACK;若处理失败,不发ACK,消息会重新入队,避免消息没处理完就被删除。

        14. RabbitMQ是如何解决消息堆积问题的?

        消息堆积是“生产快、消费慢”导致队列满,解决方案:

        • 临时扩容:增加消费者实例,并行消费(前提是队列支持多消费者)。

        • 优化消费速度:消费者批量拉取消息、异步处理、优化业务逻辑(如减少DB操作)。

        • 死信队列+延迟重试:堆积的消息超过一定时间,进入死信队列,后续慢慢重试,避免阻塞正常队列。

        • 监控预警:实时监控队列长度,超过阈值报警,提前扩容或限流。

        • 高效序列化:用Protobuf替代JSON,减少消息大小,加快传输和处理。

        http://www.dtcms.com/a/515786.html

        相关文章:

      • 网站字体大小是多少珠海网站管理公司
      • wordpress鼠标经过图片google seo是什么啊
      • Prometheus+Grafana实现Springboot服务监控
      • 保定企业建站程序设计类专业就业前景怎么样
      • centos7部署 Prometheus 3.0.0 + Grafana 10.3.3 + Alertmanager 0.27.0
      • IPv4与IPv6的对比
      • 如何查看 ubuntu 系统信息
      • 「用Python来学微积分」问题,二维图形关于任意直线的对称变换
      • php网站开发的发展前景安徽建设工程信息网网
      • R语言:非平稳时间序列实例
      • iis 添加网站 win7ip网站架设
      • AlphabetIndexer组件 与 List 联动总结
      • C++ 模拟真人鼠标轨迹算法 - 非贝塞尔曲线
      • 解锁高性能音视频处理:鸿蒙Next AVCodec Kit全解析
      • 购买服务器,操作系统选Debian、Ubuntu 还是Rocky Linux?
      • Process Monitor 学习笔记(5.23):把自定义“日志/标记”注入 Procmon 追踪
      • 专业集团门户网站建设服务商云落 wordpress
      • 基于MemU的自主代理记忆管理系统:技术解析与实践
      • 【ABAP函数】+CSAP_MAT_BOM_MAINTAIN更改BOM长文本
      • 小学英语资源合集
      • 裴蜀定理(Bézout‘s identity)
      • 在昇腾NPU上跑Llama 2模型:一次完整的性能测试与实战通关指南
      • 用wordpress建站一个人可以吗wordpress主题 表白
      • 鸿蒙6.0技术解析:五大行业迎来的智能化革命
      • Java的单例设计模式-饿汉式
      • 帝国建设网站phpcms做网站感想
      • 我的应用 Full GC 频繁,怎么优化?
      • clickhouse副本只有一个节点有数据原因
      • 【MySQL】从零开始了解数据库开发 --- mysql事务机制(二)
      • 国外手表网站湖南省专业建设公司网站