java八股文-(spring cloud)微服务篇-参考回答

发布于:2025-08-18 ⋅ 阅读:(14) ⋅ 点赞:(0)

一. 面试官:Spring Cloud 5大组件有哪些?

Spring Cloud 的五大核心组件包括:

  1. 注册中心:第一代使用 Eureka,第二代使用 Nacos
  2. 负载均衡:第一代使用 Ribbon,第二代使用 Spring Cloud LoadBalancer
  3. 服务调用:第一代使用 Feign,第二代使用 OpenFeign
  4. 熔断限流:第一代使用 Hystrix,第二代使用 Sentinel(或者Hystrix的spring官方替代方案Resilience4j
  5. 网关:第一代使用 Zuul,第二代使用 Spring Cloud Gateway

目前主流推荐使用第二代组件( Spring Cloud Alibaba 套件: Nacos、Sentinel、Gateway),因其性能更高、生态更活跃,且更适配云原生场景。


选型建议

  • 传统项目:若依赖 Netflix 组件(如 Eureka、Hystrix),可继续维护。
  • 新项目:推荐使用 Spring Cloud Alibaba 套件( Nacos、Sentinel、Gateway)+Spring Cloud 原生替代方案(LoadBalancer、OpenFeign :
    • 注册中心:Nacos(替代 Eureka)
    • 负载均衡:LoadBalancer(替代 Ribbon)
    • 服务调用:OpenFeign(替代 Feign)
    • 熔断限流:Sentinel/Resilience4j(替代 Hystrix)
    • 网关:Spring Cloud Gateway(替代 Zuul)

1. 服务注册与发现

  • 第一代Eureka(Netflix 开源)
    • 作用:服务注册与发现,管理微服务实例的注册、心跳、剔除。
    • 特点:C/S 架构,服务实例通过心跳维持可用性(默认 30 秒一次),若 90 秒未收到心跳则剔除。
  • 第二代Nacos(阿里开源)
    • 作用:服务注册中心 + 动态配置中心,支持临时实例/永久实例、健康检查、动态 DNS。
    • 优势:更灵活(支持多语言、动态配置、云原生场景),已逐步替代 Eureka。

2. 客户端负载均衡

  • 第一代Ribbon(Netflix 开源)
    • 作用:客户端负载均衡,结合服务注册中心(如 Eureka)选择服务实例。
    • 策略:轮询、随机、权重等。
    • 现状:已进入维护阶段。
  • 第二代Spring Cloud LoadBalancer(Spring 官方替代方案)
    • 作用:Ribbon 的替代品,功能类似但更轻量。
    • 现状:新项目推荐使用。

3. 声明式服务调用

  • 第一代Feign(Netflix 开源)
    • 作用:基于注解的 HTTP 客户端,简化服务间调用(底层依赖 Ribbon 负载均衡)。
    • 现状:Netflix 停止维护,但社区仍广泛使用。
  • 第二代OpenFeign(Spring Cloud 原生支持)
    • 作用:Feign 的升级版,与 Spring 生态深度集成,支持更多扩展(如日志、拦截器)。
    • 现状:主流选择。

4. 熔断与限流

  • 第一代Hystrix(Netflix 开源)
    • 作用:服务熔断、降级、隔离(线程池隔离),防止雪崩效应。
    • 现状Netflix 已停止维护,社区推荐替代方案。
  • 第二代Sentinel(阿里开源)
    • 作用:熔断、限流(QPS/线程数)、系统负载保护、动态规则配置。
    • 优势:高性能、支持多语言(Java/Go/C++),开箱即用 Dashboard(仪表盘)。
    • 现状:主流选择,尤其适合高并发场景。
  • 第二代Resilience4j(Spring Cloud 原生组件,官方Hystrix替代方案

    • 作用:熔断、限流、重试、缓存等,支持响应式编程(Reactive Streams)。
    • 优势:轻量级、模块化设计(功能可独立使用),与 Spring Cloud LoadBalancer 和 OpenFeign 深度集成。
    • 现状:主流选择,适合云原生和响应式架构场景。

补充说明

  • Resilience4j 的定位
    • 替代 Hystrix:作为第二代组件,Resilience4j 提供了更轻量、更灵活的服务容错能力。
    • 与 Sentinel 的区别
      • 动态配置:Resilience4j 需结合配置中心(如 Nacos)实现动态规则,而 Sentinel 提供开箱即用的 Dashboard。
      • 性能:Resilience4j 无线程池隔离,资源消耗更低,但 Sentinel 在高并发限流场景表现更优。
    • 适用场景
      • Resilience4j:适合对性能敏感、需要模块化容错能力的场景(如响应式微服务)。
      • Sentinel:适合需要动态规则配置和多语言支持的场景(如混合技术栈环境)。

5. 网关

  • 第一代Zuul(Netflix 开源)
    • 作用:API 网关,路由转发、过滤请求、限流鉴权。
    • 现状:已停更,社区活跃度低。
  • 第二代Spring Cloud Gateway
    • 作用:基于 Reactor 模型的高性能网关,支持 Predicates(路由条件)和 Filters(过滤器链)。
    • 优势:轻量、易扩展,与 Spring 生态无缝集成。
    • 现状:主流选择。

二.  Spring Cloud Alibaba 有哪些核心组件?

官方定义的五大核心组件是:

  1. Nacos(服务注册与配置中心)(替代 Eureka + Spring Cloud Config):服务注册、配置管理、动态更新
  2. Sentinel(限流与熔断)(替代 Hystrix):限流规则、熔断降级、Dashboard 使用。
  3. Seata(分布式事务)(替代 Atomikos/XA):分布式事务的 AT/TCC 模式、全局事务注解
  4. RocketMQ(分布式消息队列)(替代 Kafka/RabbitMQ):消息生产/消费、事务消息、死信队列。
  5. Dubbo(高性能 RPC 框架)(替代 Feign/Ribbon):RPC 调用、负载均衡、服务治理。

  • 高并发场景:使用 Sentinel 限流 + Nacos 动态配置。
  • 分布式事务场景:使用 Seata 的 AT 模式保证跨服务一致性。
  • 异步通信场景:使用 RocketMQ 解耦服务

(1) Nacos(服务注册与配置中心)

  • 作用:服务注册与发现、动态配置管理。
  • 面试题
    • 如何解决服务雪崩问题?(通过 Sentinel 限流 + Nacos 动态配置熔断阈值)
    • Nacos 与 Eureka 的区别?(Nacos 支持配置管理,Eureka 仅支持服务注册)

(2) Sentinel(限流与熔断)

  • 作用:流量控制、熔断降级、系统负载保护。
  • 面试题
    • 如何通过 Sentinel 实现 API 接口的限流?(使用 @SentinelResource 注解 + 配置规则)
    • Sentinel 的 Dashboard 如何动态调整规则?(通过 Sentinel 控制台实时修改 QPS 阈值)

(3) Seata(分布式事务)

  • 作用:分布式事务解决方案(AT/TCC/Saga 模式)。
  • 面试题
    • 什么是 AT 模式?它如何保证事务一致性?(基于两阶段提交,通过全局锁和回滚日志实现)
    • Seata 与 XA 协议的区别?(XA 是传统分布式事务协议,Seata 提供了更灵活的 AT/TCC 模式)

(4) RocketMQ(分布式消息队列)

  • 作用:异步通信、消息队列、削峰填谷。
  • 面试题
    • RocketMQ 如何保证消息的可靠性?(通过生产者重试、消费者确认机制、消息持久化)
    • RocketMQ 与 Kafka 的区别?(RocketMQ 支持事务消息,Kafka 更适合高吞吐量场景)

(5) Dubbo(高性能 RPC 框架)

  • 作用:高性能 RPC 调用(替代 Feign/Ribbon)。
  • 面试题
    • Dubbo 的负载均衡策略有哪些?(随机、轮询、最少活跃调用)
    • Dubbo 与 OpenFeign 的区别?(Dubbo 基于 TCP 协议,性能更高;OpenFeign 基于 HTTP,更易与 RESTful 接口兼容)

三. Spring Cloud Alibaba 和 Spring Cloud 原生组件如何选择?

  • Spring Cloud Alibaba:适合需要高并发、分布式事务、云原生支持的场景(如电商、金融)。
    • 云原生支持:Nacos 和 Sentinel 等组件与阿里云深度集成,适合高并发、高可用场景。
    • 功能扩展性:例如 Dubbo 支持多语言通信,Seata 提供 AT/TCC/Saga 多种事务模式。
  • Spring Cloud 原生组件:适合轻量级微服务架构(如小型项目或对云原生依赖较低的场景)
    • Netflix 组件已停更(如 Eureka、Hystrix),Spring Cloud Alibaba 提供了更现代的替代方案(如 Nacos、Sentinel)。

总结:

  • Spring Cloud Alibaba 的五大核心组件 是阿里巴巴开源的解决方案,专注于 云原生、高并发、分布式事务 场景。
  • Spring Cloud 原生替代方案 是社区推荐的标准化组件,适用于 通用微服务架构
  • 以哪个为准 取决于项目需求:
    • 需要阿里云能力或高并发场景 → 优先使用 Spring Cloud Alibaba 的组件。
    • 需要标准化和轻量级架构 → 优先使用 Spring Cloud 原生替代方案。
    • 混合使用 可兼顾两者优势,灵活应对复杂业务需求。

四. Spring Cloud Alibaba 其他面试题?

1. 如何将 Sentinel 与 Spring Cloud Gateway 集成?

  1. 添加依赖:spring-cloud-starter-gateway + sentinel-spring-cloud-gateway-adapter
  2. 配置 Sentinel 规则(如 application.yml 中设置限流阈值)。
  3. 通过 Sentinel Dashboard 动态调整网关路由的限流策略。

2. 如何优化 Nacos 的性能?

  • 集群部署:使用 Nacos 集群提高可用性和性能。
  • 分组隔离:通过 namespace 和 group 分离不同环境的配置和服务。
  • 减少配置更新频率:避免频繁推送配置导致网络开销。

3. 如何用 Nacos 实现服务注册与配置管理?

  • 服务注册:通过 @EnableDiscoveryClient 注解将服务注册到 Nacos。
  • 配置管理:在 Nacos 控制台创建配置文件(如 dataId),应用通过 @Value 或 @ConfigurationProperties 动态读取配置。

4. Nacos的服务注册表结构是怎样的?

  • Nacos采用了数据的分级存储模型,
  • 最外层是Namespace,用来隔离环境。
  • 然后是Group,用来对服务分组。
  • 接下来就是服务(Service)了,一个服务包含多个实例,但是可能处于不同机房,因此Service下有多个集群(Cluster),
  • Cluster(集群)下是不同的实例(Instance)。

  • 对应到Java代码中,Nacos采用了一个多层的Map来表示。
  • 结构为Map<String, Map<String, Service>>,
  • 其中最外层Map的key就是namespaceId,值是一个Map。
  • 内层Map的key是group拼接serviceName,值是Service(服务)对象。
  • Service对象内部又是一个Map,key是集群名称,值是Cluster对象。
  • 而Cluster对象内部维护了Instance的集合。

5. Nacos如何支撑阿里内部数十万服务注册压力(nacos的优势)?

  • 异步处理与阻塞队列:Nacos内部接收到注册的请求时,不会立即写数据,而是将服务注册的任务放入一个阻塞队列就立即响应给客户端。然后利用线程池读取阻塞队列中的任务,异步来完成实例更新,从而提高并发写能力。
  • 线程池调度:Nacos 使用单线程的线程池(如 SingleThreadEventExecutor)顺序执行阻塞队列中的任务,确保线程安全顺序性
  • CopyOnWrite 技术(读写分离):

    • Nacos 在更新服务实例列表时,采用CopyOnWrite写时复制)策略:首先将的实例列表拷贝一份,然后更新拷贝的实例列表,再用更新后的实例列表来覆盖旧的实例列表。

    • 读取时直接访问旧数据,避免读写冲突。写操作仅影响副本,不影响读性能。
  • 局部锁优化

    • 提升并发性能:Nacos 对每个服务独立加锁(而非全局锁),仅当同一服务的多个实例并发更新时才串行化处理。不同服务的更新操作互不干扰,减少锁竞争,支持高并发更新。

    • 服务隔离:避免级联故障,增强系统稳定性。

  • 集群与负载均衡:支持多节点集群部署,通过负载均衡分担服务注册压力。

  • 数据存储优化:内存缓存降低 I/O 开销,提升响应速度。持久化存储确保数据可靠性,避免宕机丢失。

  • 健康检查与心跳机制:动态健康检测,快速剔除异常节点,避免无效服务调用。

  •  服务发现机制:订阅推送与定时拉取。长轮询机制,客户端通过 长轮询(Long Polling)定期拉取服务实例列表的变更,而非实时推送

6. Nacos如何避免并发读写冲突问题?

  • Nacos在更新实例列表时,会采用CopyOnWrite技术,首先将旧的实例列表拷贝一份,
  • 然后更新拷贝的实例列表,再用更新后的实例列表来覆盖旧的实例列表。
  • 这样在更新的过程中,就不会对读实例列表的请求产生影响,也不会出现脏读问题了

 CopyOnWrite 技术

  • 读写分离
    • Nacos 在更新服务实例列表时,采用CopyOnWrite(写时复制)策略:

      1. 将当前实例列表复制一份(旧数据)。
      2. 在副本中修改数据(如新增/删除实例)。
      3. 用更新后的副本替换旧列表。
    • 优势
      • 读操作无锁:读取时直接访问旧数据,避免读写冲突。
      • 写操作隔离:写操作仅影响副本,不影响读性能。

7. Nacos与Eureka的区别有哪些?

Eureka 是“单一注册中心”,Nacos 是“注册 + 配置一体化平台”

维度 Eureka(Netflix) Nacos(阿里)
功能范围 仅服务注册/发现 注册发现 + 配置中心 + 动态 DNS
CAP 策略 只能 AP高可用性,牺牲一致性) AP(高可用性)/CP(强一致性 ) 动态切换
健康检查 客户端固定 30s 心跳 • 临时实例:客户端 5s 心跳
服务变更感知 客户端 30s 定时拉取(短连接) 基于 Netty 长连接
自我保护 全局开关:当续约失败比例 > 阈值,整站保护 按服务粒度计算健康比例,
实例类型 只有临时实例 临时 / 永久实例可选;
集群方式 对等节点 HTTP 复制 • AP:Distro 异步复制
生态与维护 Netflix 1.x 停止维护;2.x 无开源计划 阿里持续迭代,社区活跃,

8. Sentinel的限流与Gateway的限流有什么差别?

  • Gateway 的限流是“网关层面粗粒度、基于 Redis 的令牌桶,控制全局流量;”,
  • Sentinel 的限流是“服务治理层面细粒度、支持滑动窗口、漏桶、令牌桶等多种算法、支持熔断降级,实现精细化保护
    • 默认限流模式是基于滑动时间窗口算法

    • 排队等待的限流模式则基于漏桶算法

    • 而热点参数限流则是基于令牌桶算法

特性 Sentinel Gateway
限流算法 滑动窗口、漏桶、令牌桶(支持多模式) 令牌桶(依赖 Redis)
资源粒度 细粒度(接口、参数、调用链路) 粗粒度(接口或路径级别)
动态调整 支持运行时动态更新规则 通常需重启或重新加载配置
依赖组件 无需 Redis(本地限流) 依赖 Redis(分布式限流)
熔断降级 支持(熔断、降级、系统保护) 不支持(需结合其他组件)
性能开销 低(本地计算,无网络依赖) 高(需与 Redis 通信)
适用层级 应用层(微服务内部) 网关层(系统入口)

9. 那你介绍一下,滑动窗口、漏桶、令牌桶算法?

一、滑动窗口算法:解决固定窗口临界问题的动态计数方案

  • 思想:将固定时间窗口(如5秒)划分为多个等间隔的小区间(如5个1秒的格子),每个格子独立记录请求数。窗口随时间推移按格子粒度滑动,累计当前窗口内所有格子的请求总数,若超过阈值则触发限流
  • 优点:
    • 高精度限流:通过细化时间片,避免固定窗口在临界点的流量突增问题(如每秒末尾和下秒开头的流量叠加)。
    • 简单高效:仅需维护窗口内的计数器,适合单机限流场景。
  • 缺点:
    • 否决式限流:超出阈值的请求直接拒绝,无法阻塞等待(如排队)。
    • 无法应对突发流量:严格限制请求速率,不支持突发流量的临时过载。

二、漏桶算法:强制匀速流出的刚性限流机制

  • 思想
    • 想象一个底部匀速漏水的桶。请求像水一样倒进来,桶满了就溢出(拒绝)
    • 出口流速恒定 = 系统处理能力。
  • 关键点:桶容量 = 最大突发量;漏水速率 = 固定处理速率。
  • 优点:绝对平滑,下游不会被打爆。
  • 缺点:无法应对突发流量(桶满就丢),利用率低。

三、令牌桶算法:允许突发流量的弹性限流机制

  • 思想:有一个“发牌员”以固定速率往桶里放令牌,桶最多放 N 个。请求来了先拿令牌,拿到就通过,拿不到就拒绝或等待。
  • 关键点:放令牌速率 = 平均速率;桶大小 = 最大突发量。
  • 优点:允许一定程度的突发(桶里有令牌就能一口气放),吞吐更高,互联网最常用。
  • 缺点:突发过大仍可能冲击下游,需要配合最大并发或熔断。

10. Sentinel的线程隔离与Hystix的线程隔离有什么差别?

  • Hystrix 的线程池隔离:适合需要强隔离和超时控制的场景,但资源开销较大。
  • Sentinel 的信号量隔离:轻量高效,适合高并发、高扇出场景,但隔离性较弱。
维度 Hystrix 线程池隔离 Sentinel 信号量隔离
实现原理 每个依赖资源独占一个线程池,调用被包装成独立线程执行 只维护一个计数器(信号量),在调用线程里直接执行
资源消耗 需要大量额外线程,CPU 上下文切换多,开销大 无额外线程,开销极小
功能差异 支持主动超时、异步调用;隔离性最强 不支持主动超时/异步;隔离性较弱,但配合熔断降级可弥补

五. 服务注册和发现是什么意思?Spring Cloud 如何实现服务注册发现?

  • 我理解的是主要三块大功能,分别是服务注册 、服务发现、服务状态监控
  • 服务注册::当一个服务实例启动时,它会将自己的信息(如服务名称、IP地址、端口号、健康状态等)注册到一个 中心化的注册中心(Service Registry)
  • 服务发现:当一个服务需要调用另一个服务时,它会向注册中心查询目标服务的实例信息,并选择一个可用的实例进行调用。

eureka参考回答:我们当时项目采用的eureka作为注册中心,这个也是spring cloud体系中的一个核心组件

  • 服务注册:服务提供者需要把自己的信息注册到eureka,由eureka来保存这些信息,比如服务名称、ip、端口等等
  • 服务发现:消费者向eureka拉取服务列表信息,如果服务提供者有集群,则消费者会利用负载均衡算法,选择一个发起调用
  • 服务监控:服务提供者会每隔30秒向eureka发送心跳,报告健康状态,如果eureka服务90秒没接收到心跳,从eureka中剔除

nacos参考回答

  • 服务注册流程:

    • 服务提供者启动并注册:当一个微服务(服务提供者)启动时,会通过 Nacos SDKSpring Cloud Alibaba 框架自动向 Nacos Server 注册自身信息,

    • 心跳机制保持注册状:

      • 服务提供者定期(默认每 5 秒)向 Nacos Server 发送心跳,表明自身存活。

      • 心跳超时阈值(默认15秒),超时标记为非健康

      • 临时实例剔除超时(默认30秒),超时未心跳则删除。

    • 服务注销:服务正常关闭时,可主动发送注销请求,Nacos Server 会立即移除该实例记录。

  • 服务发现流程:

    • 服务消费者订阅服务:服务消费者启动时,通过 Nacos SDKSpring Cloud Alibaba 向 Nacos Server 订阅所需服务,获取服务实例列表。

    • 获取服务实例列表:消费者通过 DiscoveryClient 查询目标服务的所有可用实例

    • 负载均衡调用:消费者通过 Ribbon 或 Spring Cloud LoadBalancer 选择一个实例进行调用(如轮询、随机、权重)。

  • 健康检查与自动剔除:​​

    • 心跳检测:服务提供者定期发送心跳,Nacos 根据心跳判断实例健康状态。

      • 实例类型
        • 临时实例(默认):仅内存存储,失联后立即删除。
        • 永久实例:持久化存储,失联后仅标记为不健康,不会自动删除。
    • 主动健康检查:Nacos 可配置 HTTP/TCP 检查接口,主动验证服务可用性

六. 你们项目负载均衡如何实现的 ?

  •  “线上就是 双层负载均衡:两层完全解耦,各司其职。”
    • 网关层(Nginx/SLB)做 粗粒度、无状态 的流量入口分发;Nginx 负责把流量按权重/地域打入网关集群;        
    • 微服务层(Ribbon/LoadBalancer)做 细粒度、有状态客户端服务发现业务级别路由。网关里的 LoadBalancer 再根据业务标签、权重、灰度规则选到具体微服务实例
层级 负载均衡器 工作范围 典型功能
网关层 Nginx / SLB / K8s Ingress 公网→网关集群 SSL 终止、限流、WAF、灰度分流、TCP/HTTP 健康检查
微服务层 Ribbon / LoadBalancer / Dubbo 网关→内部各微服务 服务发现、权重、熔断、同机房优先、金丝雀、链路追踪

项目中负载均衡分为两层:

  • 网关层负载均衡:Nginx反向代理→ 分发流量到网关集群
    • 客户端 → Nginx(流量入口) → 网关集群(多实例) → 业务服务集群
    • Nginx:负责第一层负载均衡,将客户端请求分发到网关集群的不同实例,同时处理 SSL 终结、静态资源缓存等基础能力。
      1. 轮询(默认):按顺序依次分发请求到每个实例,适合实例性能相近的场景。
      2. 权重(weight):为不同实例设置权重(如性能好的服务器权重高),Nginx 按权重比例分配请求。
      3. IP 哈希(ip_hash):根据客户端 IP 计算哈希值,固定分发到同一实例,解决会话保持问题(如需要登录状态的场景)。
      4. 最少连接数(least_conn):优先将请求分配给当前连接数最少的实例,适合请求处理时间差异较大的场景。
    • 网关集群:由多个无状态网关实例(如 Spring Cloud Gateway、Zuul)组成,执行路由转发、认证授权、限流熔断等核心功能。

  • 微服务层负载均衡:客户端负载均衡(Ribbon/LoadBalancer)→ 服务间调用分发
    • 客户端负载均衡的核心思想是由服务调用方(消费者)自主选择目标服务实例,而非依赖网关或代理服务器。其流程如下:

    1. 服务发现:消费者从注册中心(如 Eureka、Nacos)拉取目标服务的实例列表 
    2. 负载策略决策:消费者根据预设算法(如轮询、随机、权重)选择实例 
    3. 直接调用:消费者直接向选中的实例发起请求(如 HTTP/RPC),无需经过中间代理
特性 Ribbon Spring Cloud LoadBalancer
维护状态 已停更(Netflix 停止维护) Spring 官方维护,持续更新
默认策略 ZoneAvoidanceRule(区域感知 + 轮询) RoundRobinLoadBalancer(轮询)
与 Nacos 集成 需手动配置 NacosRule 直接支持 NacosRule 策略
性能优化 较老版本,功能有限 基于 Reactor 实现,支持异步调用
适用场景 旧项目迁移或特定需求 新项目推荐使用,兼容 Spring 生态

1. Ribbon(Spring Cloud Netflix)

策略名称 描述
RoundRobinRule 轮询策略:按顺序依次选择可用的服务实例(默认策略)。
RandomRule 随机策略:随机选择一个可用的服务实例。
WeightedResponseTimeRule 加权响应时间策略:根据服务实例的响应时间和权重动态调整选择概率。
RetryRule 重试策略:在失败时尝试其他实例(底层使用 RoundRobinRule)。
ZoneAvoidanceRule 区域感知策略:优先选择与客户端同一区域(Zone)的实例。
AvailabilityFilteringRule 可用性过滤策略:过滤掉故障或超载的实例,再使用轮询策略。
BestAvailableRule 最小并发策略:选择当前并发请求最少的实例。

(1)一句话定位

“Ribbon 把负载均衡逻辑直接放到调用方 JVM 里,请求发出去之前就先选好目标实例。”

(2)三步落地流程

步骤 代码/配置 作用
① 拉取实例 DiscoveryClient 定时从 Eureka/Nacos 拿列表 本地缓存一份 List<Server>
② 选实例 IRule 默认 RoundRobinRule;可换成 RandomRuleWeightedResponseTimeRule 或自定义 决定这次请求去哪个 IP
③ 发请求 RestTemplate/Feign 注解 @LoadBalanced 即可 Ribbon 在底层把 http://order-service 换成真实地址

(3)面试小亮点

  • 权重负载:结合 Nacos 元数据 weight=3,自定义 NacosWeightedRule

  • 故障转移maxAutoRetriesNextServer=2 自动重试下一台。

  • 缺点:线程多、上下文切换大,官方 2020 起停更。

(4)自定义策略

通过实现 IRule 接口自定义策略(如哈希分配):

public class CustomRule implements IRule {
    @Override
    public Server choose(Object key) {
        // 自定义逻辑(如按用户 ID 哈希分配)
        return null;
    }
}

注册到 Ribbon:

@Bean
public IRule customRule() {
    return new CustomRule();
}

2. Spring Cloud LoadBalancer

策略名称 描述
RoundRobinLoadBalancer 轮询策略:按顺序依次选择可用的服务实例(默认策略)。
RandomLoadBalancer 随机策略:随机选择一个可用的服务实例。
NacosLoadBalancer Nacos 权重策略:基于 Nacos 的集群名和权重选择实例(需集成 Nacos)。
ReactiveLoadBalancer 异步策略:支持 Reactor 模式的异步调用(适用于 WebClient)。

(1)一句话定位

“去掉 Ribbon 的线程池包袱,用 Reactor + Caffeine 做轻量级客户端负载均衡。”

(2)三步落地流程

步骤 代码/配置 作用
① 拉取实例 DiscoveryClient → Nacos(或 Consul/Eureka) 缓存到 Caffeine,30 s 兜底刷新
② 选实例 默认 RoundRobinLoadBalancer;可换成 WeightedLoadBalancer 或自定义 基于权重或响应时间
③ 发请求 WebClient / OpenFeign 标注 @LoadBalanced 非阻塞 Netty 通道,性能 ↑30%

(3)面试小亮点

  • 零 Ribbon 依赖spring-cloud-starter-loadbalancer 单依赖即可。

  • 响应式友好:直接返回 Mono/Flux,天然支持 WebFlux。

  • 动态权重:Nacos 修改 weight 元数据,LoadBalancer 实时感知。

(4)自定义策略

通过实现 LoadBalancer 接口自定义策略(如按用户 ID 哈希分配):

public class CustomLoadBalancer implements LoadBalancer<ServiceInstance> {
    @Override
    public Mono<Response<ServiceInstance>> choose(Request request) {
        // 自定义逻辑(如按用户 ID 哈希分配)
        return null;
    }
}

注册到 LoadBalancer:

@Bean
public LoadBalancer<ServiceInstance> customLoadBalancer() {
    return new CustomLoadBalancer();
}

七. 什么是服务雪崩,怎么解决这个问题?

“下游一个服务/资源不可用 → 调用方线程/连接被耗尽 → 逐级蔓延,最终整个系统集体宕机。”

形象比喻:

一个收银台(服务 A)挂了 → 排队的人(请求)把隔壁收银台(服务 B)也堵死 → 整个超市(微服务集群)关门。


服务雪崩的解决核心是 防扩散、保核心

 
  1. 限流控制入口流量,避免服务过载;
  2. 熔断快速失败,阻止无效请求;
  3. 降级牺牲非核心功能,保障核心流程;
  4. 隔离划分资源池,防止故障蔓延。
  5. 缓存与兜底:提升系统韧性
  6. 监控与容灾:提前预警与恢复

        服务雪崩的本质是依赖链故障的失控传播,需通过“限流-熔断-隔离-缓存-监控”的组合策略构建防御体系。

        在Spring Cloud生态中,可结合Sentinel(限流熔断)、Resilience4j(熔断降级)、Nacos(服务发现)等组件,实现系统化的雪崩防护。

1. 流量控制:限制入口流量

  • 网关限流:通过Nginx、Spring Cloud Gateway等在入口层限制QPS,防止过载流量进入系统
  • 服务级限流:使用Sentinel、Resilience4j等工具,对单个服务设置并发线程数或请求数阈值

2. 熔断降级:隔离故障服务

  • 熔断机制:当服务错误率超过阈值(如50%),自动切断调用链路,快速返回降级响应(如默认值、缓存数据),避免故障扩散
  • 降级策略:可以直接返回一个默认结果。核心业务优先保障(如电商下单),非核心业务降级(如商品推荐),释放系统资源

3. 服务隔离:资源隔离与解耦

  • 线程池隔离:为每个依赖服务分配独立线程池,避免单个服务故障耗尽全局资源(如Hystrix的线程池隔离);
  • 信号量隔离:通过控制并发请求数(而非线程)实现轻量级隔离(如Sentinel的信号量模式)。

4. 缓存与兜底:提升系统韧性

  • 多级缓存:对热点数据(如商品信息)使用本地缓存(Caffeine)+ 分布式缓存(Redis),减少对下游服务的依赖
  • 兜底方案:设计降级接口或静态响应,确保故障时用户仍能获得基础服务(如“系统繁忙,请稍后重试”)。

5. 监控与容灾:提前预警与恢复

  • 实时监控:通过Prometheus、Grafana监控服务健康状态、响应时间、错误率,设置告警阈值
  • 容灾演练:定期进行混沌工程测试(如主动注入故障),验证限流、熔断机制的有效性。

八. 你们的微服务是怎么监控的?

在项目中,通过 SkyWalking 全链路追踪 + Prometheus 指标监控 + ELK 日志分析 的组合,我们实现了对微服务系统的全方位监控。这种方案不仅能够实时发现性能瓶颈和故障点,还能通过告警和可视化工具快速定位问题,显著提升了系统的稳定性和运维效率。

监控维度 工具/策略
服务调用链路 SkyWalking 分布式追踪,展示服务间的依赖关系和调用耗时。
性能指标 Prometheus 监控 JVM、线程池、HTTP 请求延迟等;Grafana 可视化指标趋势。
日志分析 ELK Stack 收集、存储、搜索日志,快速定位错误信息。
告警通知 Prometheus + Alertmanager 设置阈值(如 CPU 超过 80%),通过邮件/Slack 告警。
健康检查 Spring Boot 的 /actuator/health 端点配合 SkyWalking 检测服务可用性。
  1. 指标监控
    • 每个微服务都接 Spring Boot Actuator + Micrometer,把 JVM、HTTP、线程池、GC 等数据以 /actuator/prometheus 端点暴露。
    • Prometheus 每 5 s 抓取一次,Grafana 预置 黄金四指标(QPS、RT、Error、Saturation)大屏,一眼看出哪个服务慢、哪个接口报错 。

  2. 日志监控
    • 服务用 Logback → Kafka → Logstash → Elasticsearch → KibanaELK 管道,日志带 traceId,异常日志 5 s 内可检索。
    • 关键业务日志(下单、支付)额外打 结构化 JSON,Kibana 直接做聚合、告警 。

  3. 分布式链路追踪
    • 引入  SkyWalking,在每个微服务中引入 SkyWalking Agent,自动收集调用链路数据。通过 分布式追踪 快速定位故障点(例如某个订单服务的数据库调用耗时异常)。实时展示服务、服务实例、端点的调用链路(如 GET /orders 请求经过哪些微服务)。

  4. 告警体系
    • Prometheus 的 Alertmanager 负责秒级告警:P99 > 1 s、错误率 > 1 %、Pod 重启 3 次即触发。
    • 告警路由:钉钉机器人立即@值班人员,低级别异常进 Jira 自动建工单,周五统一复盘。

九. 你们项目中有没有做过限流 ? 怎么做的 ?

我们的限流方案分层设计:

  1. Nginx:拦截异常流量,保护后端服务。
  2. Gateway + Sentinel:动态路由级限流,适配微服务架构。
  3. Sentinel:精细化控制核心业务逻辑,保障服务稳定性。

通过多层限流策略的结合,既避免了单一限流层的不足,又实现了从全局到局部的全面保护

方案 适用场景 优点 缺点
Nginx(前置限流) 全局流量控制、防御恶意请求 性能高,部署简单;可拦截恶意流量,防护后端集群 无法动态调整规则;限流维度相对单一,主要基于 IP、连接数等简单维度
Gateway(网关限流) 微服务网关层:基于路由规则和 Redis 实现分布式限流。【如果需要更多配置规则、划分更细可以采用 Gateway + Sentinel 的网关限流方式】 可在服务入口做限流,支持路径、用户 ID、服务名等丰富维度;与微服务生态(如 Spring Cloud)适配性好;基于 Redis 能实现分布式场景下的限流 依赖 Redis 等中间件,增加架构复杂度;动态调整规则的灵活性相对弱(对比 Sentinel 等专业流量治理组件);高并发下,Redis 性能可能成为瓶颈
Gateway + Sentinel(网关限流:粗粒度 + 统一治理) 微服务网关层路由级限流,配合 sentinel 实现与业务代码解耦、可热更新、可观测、可熔断的完整流量治理方案 动态规则管理便捷,支持分布式;与业务解耦,可热更新规则;具备熔断等更丰富流量治理能力,可观测性好 需要引入 Sentinel 依赖,增加组件管理成本;整体学习和维护成本相对高一些
Sentinel(服务限流:细粒度 + 业务级) 核心业务接口保护 精细化控制,支持熔断降级;可针对具体业务接口、方法做细粒度限流;有完善的流量监控、熔断降级等配套能力 需要代码侵入性;主要聚焦服务内部接口,网关层、全局层的限流覆盖能力弱于专门的前置、网关限流方案

1.前置限流:Nginx边缘限流,防止恶意攻击

Nginx 作为系统的入口,负责第一层限流,主要用于防御突发流量和恶意请求。我们通过以下方式实现:

(1)控制速率:基于请求频率的限流(limit_req)

使用 limit_req_zone limit_req 模块,限制单位时间内的请求速率。例如:

用漏桶算法限制单位时间内的请求数,比如对首页接口限制每秒最多 1000 个请求,超出的放入队列等待,队列满了直接返回 503。

limit_req_zone $binary_remote_addr zone=home:10m rate=1000r/s;  # 定义IP维度的共享内存区
server {
    location /api/home {
        limit_req zone=home burst=200 nodelay;  # 突发流量最多200个,不延迟处理
    }
}
  • 适用场景:防止恶意爬虫或DDoS攻击,保护后端服务不被直接压垮。
  • 优点:性能高,响应快,适合全局流量整形。

(2)控制连接数:基于连接数的限流(limit_conn)

限制单个 IP 的并发连接数,防止恶意长连接占用资源,比如限制单 IP 最多 100 个并发连接。

limit_conn_zone $binary_remote_addr zone=conn:10m;
server {
    location /api/ {
        limit_conn conn 100; # 单IP最大100个并发连接
    }
}

2.网关限流:Gateway/Gateway+sentinel 统一业务级规则

在 API 网关层,我们针对不同服务的入口做精细化限流,维度更丰富(路径、用户 ID、服务名等);

(1)单用Spring Cloud Gateway 

基于路由规则和Redis实现分布式限流。

  • 使用 RequestRateLimiter 过滤器 + RedisRateLimiter
  • 按用户或IP维度限制请求速率。

依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- 基于 Redis 的令牌桶 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

配置:

spring:
  cloud:
    gateway:
      routes:
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/order/**
          filters:
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 100   # 每秒产生 100 个令牌
                redis-rate-limiter.burstCapacity: 200   # 桶容量 200
                key-resolver: "#{@ipKeyResolver}"       # SpEL 指定按 IP 限流

自定义 KeyResolver(例如按用户ID限流):

@Bean 
KeyResolver userKeyResolver() {
    return exchange -> Mono.just(exchange.getRequest()
                    .getQueryParams().getFirst("userId")); 
}

注意:

  • 网关层一般负责粗粒度限流(按接口、用户等级、租户等),别把单机 QPS 设得太低;

  • Redis 限流器默认使用 Lua 脚本,单节点 Redis 可扛 5 w QPS,再高就上 Redis Cluster + 本地缓存预热。

(2)Spring Cloud Gateway + sentinel 配合使用

在微服务架构中,网关层(Spring Cloud Gateway)负责对路由级别的请求进行限流,结合 Sentinel 实现动态规则管理:

集成 Sentinel
添加依赖:

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>

配置 Sentinel 控制台:

spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8774 # Sentinel 控制台地址

动态限流规则
通过 Sentinel 控制台或 Nacos 配置规则,例如:

spring:
  cloud:
    sentinel:
      scg:
        fallback:
          mode: response
          response-body: '{"code": 429, "msg": "请求过多,请稍后再试"}'
  • Route 维度:对特定路由(如 /user/login)设置 QPS 限流。
  • API 维度:通过自定义 API 分组,对路径参数(如 /order/{id})进行更细粒度的限流。

自定义限流响应
覆盖默认的限流响应,提升用户体验:

public class CustomBlockHandler implements BlockRequestHandler {
    @Override
    public Mono<ServerResponse> handleRequest(ServerWebExchange exchange, Throwable t) {
        return ServerResponse.status(HttpStatus.TOO_MANY_REQUESTS)
            .bodyValue("系统繁忙,请稍后再试");
    }
}

3. 服务层限流:sentine接口精细化限流

接入:

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
  • 作用:保护核心方法,支持熔断降级、热点参数限流等高级规则。
  • 核心功能
    • QPS/线程数限流:通过控制台动态配置规则。
    • 热点参数限流:针对高频参数(如商品ID)独立限流。
    • 熔断降级:自动阻断不稳定资源的调用。
  • 代码示例
    @SentinelResource(
        value = "queryOrder",
        blockHandler = "handleQueryOrderBlock", // 限流处理函数
        fallback = "queryOrderFallback"         // 熔断处理函数 
    )
    public Order queryOrder(String orderId) {
        // 业务逻辑 
    }
    
    // 限流处理逻辑 
    public Order handleQueryOrderBlock(String orderId, BlockException ex) {
        throw new RuntimeException("请求过于频繁,请稍后重试!");
    }
    
  • 动态规则:规则持久化到Nacos,实时生效无需重启。

十. 面试官:什么是CAP理论?

1. 30 秒“电梯回答”先介绍

  • C(一致性)
    任意时刻,所有节点看到的数据是一样的。

  • A(可用性)
    系统在任何时候都能响应请求(成功或失败),不会出现超时或拒绝服务的情况

  • P(分区容错性)
    网络出现分区(节点间断网)时,系统仍继续运行。

由于网络分区在分布式系统里一定会发生,所以 P 必须保留,剩下只能在 C 和 A 之间做取舍——要么牺牲一致性,要么牺牲可用性


2. 90 秒“项目回答”举例一下场景

举一个下单场景:

  • 如果选 CP(Redis + RedLock 分布式锁):

    • 网络一抖动就把写请求拒绝或阻塞,保证库存绝对一致,但用户可能看到“系统繁忙”。导致服务暂时不可用。

    • (保证数据一致,牺牲可用性)

  • 如果选 AP(本地库存缓存 + MQ 异步扣减):

    • 用户随时都能下单,秒杀页面不卡;极端情况下可能超卖,通过后续对账补偿。

    • (保证可用,但是不同节点数据可能不一致)


3. 120 秒“加分回答”总结

  • CAP 不是“三选二”而是“当 P 发生时,在 C 和 A 中二选一”

  • 实际系统常用“最终一致性 + 高可用”(AP + 异步补偿),如:
    – 订单系统:用户侧先返回成功,库存通过 RocketMQ 最终一致。
    – 支付系统:核心账务走 CP(XA 或 TCC),非核心记账走 AP(异步流水)。

  • BASE 理论 是对 CAP 的延伸:
    Basically Available(基本可用) + Soft state(软状态) + Eventually consistent(最终一致)。

十一. 面试官:为什么分布式系统中无法同时保证一致性和可用性?

分布式系统的本质是 “多节点通过网络协同工作”,但网络故障(分区)必然会发生的(比如光缆中断、交换机故障)。一旦出现分区,节点间就成了 “信息孤岛”,无法实时同步状态。系统必须在 立即返回结果(A可用性)确保所有节点数据一致(C一致性) 之间做出 二选一 的抉择,无法两全。

用最简单的 “写入冲突” 场景就能证明:

  1. 系统有 N1、N2 两个节点。

  2. 客户端 Cl 向 N1 写入 x = 1

  3. 网络突然断开(分区),N1 与 N2 无法通信。

  4. 此时 Cl 再次写 x = 2,但只能连到 N2
    • 如果系统 保证可用性(A):N2 必须接受写请求并立刻返回成功,于是 N1、N2 数据不一致 → 牺牲一致性。
    • 如果系统 保证一致性(C):N2 必须拒绝或阻塞写请求,等到网络恢复同步后再处理,于是 客户端得不到响应 → 牺牲可用性。

因此,在 P 发生的前提下,C 与 A 不可能同时满足;这就是分布式系统无法同时保证一致性和可用性的根本原因。

十二.  什么是BASE理论?

BASE 理论是对 CAP 中“牺牲一致性、保证可用性”这一取舍的工程落地指导,由 Basically Available(基本可用)、Soft State(软状态)、Eventually Consistent(最终一致性)三部分组成。


1. 三个单词拆开讲

缩写 全称 含义 一句话举例
BA Basically Available 系统基本可用:在故障或高并发时,允许降级、延迟、削峰,但核心功能仍可对外服务。 双十一高峰期,商品详情页把“猜你喜欢”模块关掉,只保留“立即购买”,就是基本可用。
S Soft State 软状态:允许数据在一段时间内不一致,状态可以随着时间、事件异步变化;不需要像 ACID 那样实时强一致。 订单创建后,库存先扣缓存中的预估值,1~2 分钟后再异步核对真实库存。
E Eventually Consistent 最终一致性:系统保证在没有新更新的前提下,所有副本最终会达到一致。 支付成功后,余额表、账单表、会计科目表通过消息队列最终对齐。

2. 与 CAP 的关系

  • CAP 告诉我们“P 发生时,在 C(一致性) 和 A (可用性)之间只能二选一”。

  • BASE 则给出工程实践路线
    选 APBA 保证业务不挂 → S 允许中间数据不一致 → E 通过异步机制最终纠正数据。


3. 面试 30 秒模板

“BASE 理论是 CAP 在工程里的延伸:
当网络分区出现时,我们放弃强一致性,先保证 基本可用(BA),允许 软状态(S) 短暂不一致,再通过 异步重试/消息/对账(E) 把数据最终对齐,从而兼顾高可用和可扩展性。”

十三. 你们采用哪种分布式事务解决方案?

通俗理解:

  1. Seata 系列(AT/TCC/SAGA)

    • AT:像“自动回滚”按钮,业务代码几乎不用改,适合简单场景
    • TCC:像“分步操作+后悔药”,需要手动写每一步的逻辑和回滚,适合复杂业务
    • SAGA:像“流程图+撤销键”,把大流程拆成小步骤,失败后按相反顺序撤销。
  2. 2PC/XA

    • 像“投票表决”,所有参与者必须同意才能提交,适合对一致性要求极高的场景(如银行转账)。
  3. 本地消息表 + 消息队列

    • 像“先记账再通知”,确保业务操作和消息同步,适合异步处理和解耦
  4. 独立 TCC

    • 像“手动分步操作”,完全靠开发者自己控制每一步的逻辑和回滚,适合高性能需求
模式名称 核心思想 适用场景 优势 劣势
Seata AT 模式 通过代理数据源自动记录 undo log,实现自动补偿(类似数据库回滚)。 简单业务(如订单、库存、支付),需强一致性。 低侵入性,业务代码几乎无需修改;支持自动补偿。 需依赖 Seata 框架,性能略低于 TCC/SAGA;对数据库事务要求高。
Seata TCC 模式 手动编写 Try/Confirm/Cancel 三阶段逻辑(预留资源、确认执行、回滚)。 复杂业务(如航班+酒店预订、秒杀),需精确控制。 高性能,Try 阶段快速响应;支持复杂业务流程。 开发成本高,需手动实现补偿逻辑;需处理幂等性和重试问题。
Seata SAGA 模式 将长事务拆分为多个本地事务,通过状态机管理补偿(如退库存、退款)。 长周期业务(如物流跟踪、审批流程)。 高可用性,无需协调者;支持复杂流程。 需手动编写补偿逻辑;最终一致性,短时间不一致。
2PC/XA(强一致性) 协调者统一调度所有参与者,分为准备阶段和提交/回滚阶段(XA 协议依赖数据库)。 金融系统(如银行转账、跨账户资金划转)。 强一致性,保证所有节点要么全部提交,要么全部回滚。 性能差(同步阻塞),单点故障风险;需数据库支持 XA 协议。
本地消息表 + 消息队列 通过本地消息表保证业务操作与消息发送的原子性(如 RocketMQ 事务消息)。 订单状态同步、异步通知(如库存扣减通知)。 高可靠性,消息队列保障最终一致性;生产者与消费者解耦。 实现复杂(需维护本地消息表和消息队列事务);异步处理导致短时间不一致。
TCC(独立实现) 手动实现 Try/Confirm/Cancel 逻辑(类似 Seata TCC,但不依赖框架)。 高并发场景(如秒杀、库存扣减)。 高性能,Try 阶段快速响应;灵活控制资源预留和回滚。 开发成本高,需处理幂等性和补偿逻辑;需自行维护事务状态。

1. Seata(AT/TCC/SAGA 模式)

核心思想

  • AT模式:通过代理数据源,在业务SQL执行前后自动记录数据快照(undo log),实现自动补偿。

  • TCC模式:开发者手动编写 Try、Confirm、Cancel 三个阶段的逻辑。

  • SAGA模式:将长事务拆分为多个本地事务,通过状态机管理补偿流程。

典型场景

  • 电商系统:订单创建、库存扣减、支付扣款等跨服务操作。

    • AT模式:订单服务调用库存服务和支付服务,Seata自动处理回滚。

    • TCC模式:复杂业务(如航班+酒店预订)需要精确控制补偿逻辑。

    • SAGA模式:长周期业务(如物流跟踪)通过状态机逐步推进。

优势

  • 低侵入性:AT模式几乎无需修改业务代码。

  • 高性能:一阶段本地提交,避免长时间资源锁定。

  • 多模式支持:灵活应对不同场景需求。

劣势

  • 依赖Seata框架:需引入中间件,增加运维成本。


2. 两阶段提交(2PC/XA)

核心思想

  • 协调者统一调度所有参与者,分为准备阶段和提交/回滚阶段。

  • XA协议:基于数据库的分布式事务支持(如MySQL、Oracle)。

典型场景

  • 金融系统:银行转账、跨账户资金划转。

    • 例如:A账户转钱到B账户,需同时更新两个账户的数据库。

优势

  • 强一致性:保证所有节点要么全部提交,要么全部回滚。

  • 成熟稳定:XA协议被主流数据库支持。

劣势

  • 性能瓶颈:同步阻塞导致吞吐量下降。

  • 单点故障:协调者宕机可能导致事务卡死。


3. Saga 模式

核心思想

  • 将长事务拆分为多个本地事务,每个步骤可独立提交。

  • 若某一步骤失败,按相反顺序执行补偿操作(如退库存、退款)。

典型场景

  • 物流系统:订单状态同步、多仓库库存分配。

    • 例如:用户下单后,依次调用库存服务、物流服务、支付服务,若物流失败则回退库存和支付。

优势

  • 高可用性:无需协调者,避免单点故障。

  • 灵活性:支持复杂业务流程。

劣势

  • 开发复杂度高:需手动编写补偿逻辑。

  • 最终一致性:可能短时间不一致,需业务容忍。


4. 本地消息表 + 消息队列(如 RocketMQ 事务消息)

核心思想

  • 通过本地消息表保证业务操作与消息发送的原子性。

  • 消息队列(如 RocketMQ)提供事务消息机制,确保消息可靠投递。

典型场景

  • 订单状态同步:订单服务更新状态后,异步通知库存服务。

    • 例如:用户支付成功后,订单服务先写入本地消息表,再发送消息到 RocketMQ,库存服务消费消息后扣减库存。

优势

  • 高可靠性:消息队列保障最终一致性。

  • 解耦:生产者与消费者解耦,提升系统扩展性。

劣势

  • 实现复杂:需维护本地消息表和消息队列的事务一致性。

  • 延迟:异步处理导致短时间不一致。


5. TCC 模式

核心思想

  • Try:预留资源(如冻结库存)。

  • Confirm:确认执行(如扣减库存)。

  • Cancel:回滚操作(如释放库存)。

典型场景

  • 秒杀系统:高并发下的库存扣减。

    • Try阶段冻结库存,Confirm阶段扣减,Cancel阶段释放。

优势

  • 高并发:Try阶段快速响应,避免资源锁定。

  • 灵活性:适用于复杂业务逻辑。

劣势

  • 开发成本高:需手动实现 Try/Confirm/Cancel 逻辑。

  • 补偿逻辑复杂:需考虑幂等性和重试机制。

十四. 分布式服务的接口幂等性如何设计?

什么是接口幂等性?

  • 定义:无论调用多少次同一个接口,对系统资源的影响都与第一次调用一致。
  • 核心目标:防止重复请求导致的副作用(如重复扣款、重复下单、数据不一致等)。

幂等性设计的常见场景

  1. 用户误操作:用户多次点击提交按钮。
  2. 网络问题:请求超时后客户端重试。
  3. 服务重试:微服务调用失败后的自动重试。
  4. 消息队列:消息重复消费(如 Kafka、RabbitMQ)。

实际开发建议

  1. 优先选择组合方案:例如支付接口结合 Token 和数据库唯一索引。
  2. 合理设置过期时间:避免 Redis 中的 Token 或锁长期占用资源。
  3. 异常处理:捕获数据库异常(如 DuplicateKeyException)并返回友好提示。
  4. 日志记录:记录重复请求的详细信息,便于排查问题
方案 适用场景 优点 缺点
Token 机制 表单提交、支付接口 简单高效,天然防重 需维护 Token 生命周期
唯一约束 新增操作 数据库层直接拦截 仅适用于插入场景
乐观锁 更新操作 高并发下避免锁竞争 需额外维护版本号字段
分布式锁 强一致性场景 强一致性保障 性能开销大
请求唯一标识 高频接口 灵活控制请求状态 需存储大量请求 ID

    1. 主流解决方案

    (1)Token 机制(推荐通用方案)

    • 原理:为每个请求生成唯一 Token,服务端验证 Token 有效性并原子化删除,防止重复提交。
    • 实现步骤
      1. 客户端请求获取 Token(服务端用 Redis 存储 Token,设置过期时间)。
      2. 客户端提交业务请求时携带 Token。
      3. 服务端通过 Redis 的原子操作(如 DEL 或 Lua 脚本)验证并删除 Token。
    • 适用场景:表单提交、支付接口等外部调用。
    • 代码示例(Spring Boot)
      @Aspect
      @Component
      public class IdempotentAspect {
          @Around("@annotation(Idempotent)")
          public Object checkToken(ProceedingJoinPoint pjp) {
              String token = request.getHeader("Idempotent-Token");
              if (!redisTemplate.delete("idempotent:token:" + token)) {
                  throw new RuntimeException("重复请求");
              }
              return pjp.proceed();
          }
      }
    • 优点:简单高效,天然防重。
    • 缺点:需维护 Token 生命周期,需处理 Redis 异常。

    (2)数据库唯一约束

    • 原理:利用数据库唯一索引阻止重复数据写入。
    • 实现方式
      • 为关键字段(如订单号、用户手机号)添加唯一索引。
      • 插入数据时捕获 DuplicateKeyException 异常。
    • 适用场景:用户注册、订单创建等新增操作。
    • 代码示例
      CREATE UNIQUE INDEX uniq_order_no ON orders(order_no);
      try {
          orderDao.insert(order);
      } catch (DuplicateKeyException e) {
          log.warn("重复订单:{}", order.getOrderNo());
          return Result.error("订单已存在");
      }
    • 优点:数据库层直接拦截,无需额外代码。
    • 缺点:仅适用于插入场景,无法覆盖更新操作。

    (3) 乐观锁(版本号控制)

    • 原理:通过版本号(或时间戳)实现原子性更新,确保仅当数据未变更时才执行。
    • 实现方式
      • 表中增加 version 字段,更新时校验版本号。
      • 示例 SQL:
        UPDATE orders SET status = 'PAID', version = version + 1 
        WHERE id = 1 AND version = 1;
    • 适用场景:更新操作(如库存扣减、订单状态变更)。
    • 优点:高并发下避免锁竞争。
    • 缺点:需额外维护版本号字段。

    (4)分布式锁

    • 原理:通过分布式锁(如 Redis 的 setNx 或 Redlock)控制同一请求的并发执行。
    • 实现方式
      • 使用 Redis 的 setNx 操作尝试获取锁。
      • 示例代码:
        String lockKey = "order:lock:" + orderId;
        Boolean isLocked = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
        if (Boolean.TRUE.equals(isLocked)) {
            try {
                // 执行业务逻辑
            } finally {
                redisTemplate.delete(lockKey);
            }
        } else {
            throw new RuntimeException("重复请求");
        }
    • 优点:强一致性保障。
    • 缺点:性能开销较大,需处理锁超时和死锁问题。

    (5)请求唯一标识 + 状态记录

    • 原理:为每个请求生成唯一标识(如 UUID),服务端记录请求状态。
    • 实现方式
      • 客户端生成唯一请求 ID 并附带在请求中。
      • 服务端查询该请求是否已处理过,若已处理则直接返回结果。
    • 适用场景:支付接口、订单创建等高频操作。
    • 代码示例
      String requestId = request.getHeader("X-Request-ID");
      if (redisTemplate.hasKey("processed:request:" + requestId)) {
          return Result.success("请求已处理");
      }
      // 处理业务逻辑
      redisTemplate.set("processed:request:" + requestId, "1", 24, TimeUnit.HOURS);

    2. 典型场景分析

    场景 1:支付接口(重复扣款)

    • 解决方案:结合 Token 机制 和 数据库唯一约束
      • 用户支付前获取 Token,服务端验证 Token 并删除。
      • 支付流水表设置唯一索引(订单号 + 交易 ID)。
    • 优势:双重保障,避免网络重试和用户误操作导致的重复扣款。

    场景 2:订单创建(重复下单)

    • 解决方案数据库唯一约束
      • 订单号设为唯一索引,插入失败则返回错误。
    • 优势:简单高效,无需额外逻辑。

    场景 3:库存扣减(并发更新)

    • 解决方案乐观锁
      • 更新时校验版本号,仅允许一次成功扣减。
    • 优势:高并发下避免超卖问题。

    网站公告

    今日签到

    点亮在社区的每一天
    去签到