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

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

一. 面试官: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 的限流是“服务治理层面细粒度、支持滑动窗口、漏桶、令牌桶等多种算法、支持熔断降级,实现精细化保护
    • 默认限流模式是基于滑动时间窗口算法

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

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

特性SentinelGateway
限流算法滑动窗口、漏桶、令牌桶(支持多模式)令牌桶(依赖 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),无需经过中间代理
特性RibbonSpring 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 {@Overridepublic Server choose(Object key) {// 自定义逻辑(如按用户 ID 哈希分配)return null;}
}

注册到 Ribbon:

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

2. Spring Cloud LoadBalancer

策略名称描述
RoundRobinLoadBalancer轮询策略:按顺序依次选择可用的服务实例(默认策略)。
RandomLoadBalancer随机策略:随机选择一个可用的服务实例。
NacosLoadBalancerNacos 权重策略:基于 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> {@Overridepublic 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-serviceuri: lb://order-servicepredicates:- Path=/order/**filters:- name: RequestRateLimiterargs:redis-rate-limiter.replenishRate: 100   # 每秒产生 100 个令牌redis-rate-limiter.burstCapacity: 200   # 桶容量 200key-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: responseresponse-body: '{"code": 429, "msg": "请求过多,请稍后再试"}'
  • Route 维度:对特定路由(如 /user/login)设置 QPS 限流。
  • API 维度:通过自定义 API 分组,对路径参数(如 /order/{id})进行更细粒度的限流。

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

public class CustomBlockHandler implements BlockRequestHandler {@Overridepublic 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. 三个单词拆开讲

缩写全称含义一句话举例
BABasically Available系统基本可用:在故障或高并发时,允许降级、延迟、削峰,但核心功能仍可对外服务。双十一高峰期,商品详情页把“猜你喜欢”模块关掉,只保留“立即购买”,就是基本可用。
SSoft State软状态:允许数据在一段时间内不一致,状态可以随着时间、事件异步变化;不需要像 ACID 那样实时强一致。订单创建后,库存先扣缓存中的预估值,1~2 分钟后再异步核对真实库存。
EEventually 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:库存扣减(并发更新)

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

    相关文章:

  1. FreeRTOS在中断服务例程(ISR)中使用队列
  2. 小白成长之路-k8s部署discuz论坛
  3. Python爬虫-解决爬取政务网站的附件,找不到附件链接的问题
  4. Blender模拟结构光3D Scanner(二)投影仪内参数匹配
  5. scikit-learn/sklearn学习|多任务套索回归MultiTaskLasso解读
  6. The Network Link Layer: 无线传感器中Delay Tolerant Networks – DTNs 延迟容忍网络
  7. C++---迭代器删除元素避免索引混乱
  8. 最长回文子串问题:Go语言实现及复杂度分析
  9. Flink Stream API核心概念继承体系
  10. 代码随想录刷题Day34
  11. 分治-归并-315.计算右侧小于当前元素的个数-力扣(LeetCode)
  12. 42 C++ STL模板库11-容器4-forward_list
  13. macos 安装nodepad++ (教程+安装包+报错后的解决方法)
  14. 深入解析函数指针及其数组、typedef关键字应用技巧
  15. HAL-EXTI配置
  16. Linux | i.MX6ULL网络通信-套字节 UDP(第十八章)
  17. 【OpenGL】LearnOpenGL学习笔记11 - 多光源
  18. Linux入门指南:基础开发工具---vim
  19. mysql建库规范
  20. 《详解 C++ Date 类的设计与实现:从运算符重载到功能测试》
  21. 基于Vue + Node能源采购系统的设计与实现/基于express的能源管理系统#node.js
  22. 数据结构与算法:线段树(一):基本原理
  23. 【Python练习】097. 编写一个函数,实现简单的版本控制工具
  24. 机器人经验学习1 杂记
  25. 牛客周赛 Round 105
  26. Vue 与 React 深度对比:设计哲学、技术差异与应用场景
  27. 深度学习·GFSS
  28. 基于RK3588的微电网协调控制器:实现分布式能源的智能调控与优化运行
  29. JavaScirpt高级程序设计第三版学习查漏补缺(1)
  30. MysqL(二:sqL调优)