Skip to content

本轮 SpringCloud微服务架构面试题库(带答案) ​

生成时间:2026-05-22 22:26 | 共10题


【面试题】1. Eureka的自我保护机制是什么?什么场景下会触发?生产环境建议开启还是关闭? ​

【标准答案】Eureka自我保护机制是:当Eureka Server在短时间内丢失超过一定比例的心跳(默认85%),会认为不是服务本身挂了,而是网络出了问题,于是不再剔除过期服务实例。

触发条件:Eureka Server每分钟期望收到的心跳数 × 0.85 > 实际收到的心跳数,持续一段时间后触发。

核心原理:Eureka遵循AP模型,宁可保留可能过期的数据,也不随意删除,因为网络抖动比服务宕机更常见。进入自我保护后,Server不再从注册表中移除任何实例,即使心跳超时也不踢。

生产环境建议:一般建议开启。因为生产环境网络抖动很常见,如果关闭自我保护,一旦网络波动,大量服务会被误判下线,导致流量全部涌向存活服务,引发雪崩。但如果你的环境网络非常稳定,且对服务状态的实时性要求极高(比如金融交易场景),可以考虑关闭,配合健康检查做精准摘除。


【面试题】2. Ribbon和Spring Cloud LoadBalancer的核心区别是什么?为什么Spring Cloud要替换Ribbon? ​

【标准答案】核心区别有三点:

  1. 技术栈不同:Ribbon基于Netflix开源,依赖较多,维护已停止(进入维护模式);Spring Cloud LoadBalancer是Spring官方出品,轻量级,只依赖spring-cloud-commons。

  2. 缓存机制不同:Ribbon有独立的ServerList缓存,默认30秒刷新一次,服务上下线感知有延迟;LoadBalancer直接基于ServiceInstanceListSupplier获取实例,默认35秒缓存,但可以自定义更灵活的刷新策略。

  3. 扩展方式不同:Ribbon通过ILoadBalancer接口+IRule策略扩展,配置项多且分散(ribbon.xxx);LoadBalancer通过Reactive编程模型+ServiceInstanceListSupplier扩展,配置简洁,和Spring Cloud Gateway天然集成。

替换原因:Ribbon停更,社区不再修复Bug和安全漏洞;Ribbon的缓存和重试逻辑复杂,容易踩坑;Spring Cloud要统一技术栈走向Reactive响应式,LoadBalancer天生支持WebFlux。

迁移成本很低——大部分场景只需把ribbon.xxx配置换成spring.cloud.loadbalancer.xxx,自定义策略实现ReactorServiceInstanceLoadBalancer接口即可。


【面试题】3. OpenFeign的超时重试配置怎么设?有什么踩坑点? ​

【标准答案】OpenFeign超时分两层:连接超时(connectTimeout)和读取超时(readTimeout)。

配置方式:

yaml
feign:
  client:
    config:
      default:                          # 全局默认
        connectTimeout: 5000            # 连接超时5秒
        readTimeout: 10000              # 读取超时10秒
      order-service:                    # 针对特定服务
        connectTimeout: 3000
        readTimeout: 5000

踩坑点:

  1. Ribbon和Feign超时谁生效? 如果同时配了Ribbon超时,实际生效的是两者中更小的那个。LoadBalancer模式下只有Feign超时生效。

  2. 重试默认关闭:Feign默认不重试。要开启需配置Retryer,但要注意和Ribbon重试叠加——如果两者都开了重试,最坏情况下重试次数会相乘。建议只在一层开重试。

  3. 读超时不能设太长:读超时过长会导致线程长时间占用,高并发下拖垮连接池。一般读超时设为P99响应时间的2-3倍。

  4. 非幂等接口不要重试:POST等非幂等请求重试可能导致数据重复提交。可以配合feign.httpclient.disableSslValidation和自定义重试策略,对POST请求跳过重试。

  5. 超时和熔断联动:如果Sentinel/Hystrix的超时比Feign短,熔断会先触发,Feign超时配置形同虚设。确保熔断超时 > Feign超时。


【面试题】4. Sentinel的熔断状态机是怎么运转的?三个状态之间的转换条件是什么? ​

【标准答案】Sentinel熔断状态机有三个状态:CLOSED(关闭)→ OPEN(打开)→ HALF-OPEN(半开)。

CLOSED(关闭状态):正常放行所有请求。Sentinel持续统计指标(慢调用比例、异常比例、异常数),当指标超过阈值,切换到OPEN。

OPEN(打开状态):所有请求直接拒绝,走降级逻辑。维持一段时间(熔断时长,默认10秒),时间到后自动切换到HALF-OPEN。

HALF-OPEN(半开状态):允许少量请求(默认1个)通过,探测下游是否恢复:

  • 如果探测请求成功 → 切回CLOSED,恢复正常流量
  • 如果探测请求仍然失败 → 切回OPEN,继续熔断

触发熔断的三种策略:

  1. 慢调用比例:响应时间超过最大RT的请求算慢调用,慢调用比例超过阈值且请求量 ≥ 最小请求数时触发
  2. 异常比例:异常数/总请求数 > 阈值,且请求量 ≥ 最小请求数时触发
  3. 异常数:单位时间内异常数 > 阈值时触发

面试加分点:注意"最小请求数"这个条件——如果10秒内只有2个请求全失败了,异常比例100%,但没到最小请求数,不会熔断。这是防止低流量误触发的保护机制。


【面试题】5. Spring Cloud Gateway的过滤器链执行顺序怎么控制?GlobalFilter和GatewayFilter有什么区别? ​

【标准答案】过滤器执行顺序由getOrder()返回值决定,数值越小优先级越高(和Spring的Ordered机制一致)。

GlobalFilter vs GatewayFilter:

维度GlobalFilterGatewayFilter
作用范围所有路由特定路由
配置方式实现GlobalFilter+Ordered接口,自动生效在路由定义中配置(yaml/Java API)
典型场景鉴权、日志、限流、跨域请求参数转换、路径重写、特定路由的降级

执行流程:请求进来 → Pre阶段(按order从小到大执行所有filter的pre逻辑) → 转发到下游 → Post阶段(按order从大到小执行所有filter的post逻辑)。

踩坑点:

  1. Pre和Post顺序相反:Pre阶段order=1的先执行,Post阶段order=1的后执行。这符合栈的LIFO特性——最先进去的最后出来。

  2. filter链中断:在Pre阶段调用exchange.getResponse().setComplete()可以直接返回响应,后续filter不再执行。常用于鉴权失败直接拒绝。

  3. 自定义order值建议:官方内置filter的order一般是0、1、1000等,自定义filter建议用-1到100之间的值,确保在关键位置插入。不要用Integer.MIN_VALUE这种极端值,容易和未来版本冲突。


【面试题】6. Nacos Config配置热更新的原理是什么?和Spring Cloud Config的热更新有什么区别? ​

【标准答案】Nacos Config热更新原理:

  1. 长轮询机制:客户端启动时向Nacos Server发起长轮询请求(默认超时30秒),Server端hold住连接。当配置变更时,Server立即响应返回新配置;如果30秒内没变更,返回304,客户端重新发起长轮询。

  2. 配置变更监听:客户端收到新配置后,通过Listener回调触发Spring Environment刷新,@RefreshScope标注的Bean会被销毁重建,@Value注解的字段会重新注入。

  3. DataId+Group定位:配置通过DataId(一般是服务名+后缀)和Group唯一定位,支持多环境隔离(namespace)。

和Spring Cloud Config的区别:

维度Nacos ConfigSpring Cloud Config
配置存储内置数据库(Derby/MySQL)Git/SVN/Vault
热更新方式长轮询,秒级感知依赖Spring Cloud Bus+MQ广播,或手动发/actuator/refresh
运维复杂度开箱即用,自带管理界面需要额外部署Bus、MQ、Git仓库
一致性模型CP(Raft协议选举Leader)依赖Git的一致性
配置回滚支持历史版本一键回滚依赖Git回滚
灰度发布原生支持Beta发布不支持,需自研

面试关键点:Nacos的配置变更是推拉结合——长轮询本质是"长连接的拉",不是纯粹的推模式,但效果等同于推送。这样既保证实时性,又不需要维护WebSocket等长连接的高开销。


【面试题】7. Seata的AT模式两阶段提交的具体过程是什么?和TCC模式相比各有什么优劣? ​

【标准答案】AT模式(自动事务):

一阶段(业务+回滚日志):

  1. 拦截业务SQL,解析SQL语义
  2. 查询修改前的数据,生成前镜像(beforeImage)
  3. 执行业务SQL
  4. 查询修改后的数据,生成后镜像(afterImage)
  5. 将前后镜像组织成undo_log记录,和业务SQL在同一个本地事务中提交
  6. 向TC(事务协调者)注册分支事务,上报分支状态

二阶段-提交:

  • TC通知各分支提交 → 异步清理undo_log即可(因为一阶段已经提交了本地事务)

二阶段-回滚:

  • TC通知各分支回滚 → 读取undo_log → 校验后镜像(脏写检查)→ 生成反向SQL执行回滚 → 删除undo_log

TCC模式(手动事务):

  • Try:预留资源(冻结金额等)
  • Confirm:确认提交(扣减冻结金额)
  • Cancel:取消回滚(解冻金额)

AT vs TCC:

维度ATTCC
侵入性零侵入,只需加@GlobalTransactional高侵入,需写Try/Confirm/Cancel三段逻辑
性能一阶段直接提交本地事务,锁持有时间短一阶段资源锁定,持有到二阶段
一致性依赖undo_log回滚,存在脏写风险业务自行保证一致性,更可靠
适用场景大多数常规业务资金类、库存扣减等强一致性场景

面试加分:AT模式的脏写检查——回滚时对比afterImage和当前数据,如果一致说明没被其他人修改过,安全回滚;如果不一致说明被脏写了,需要人工介入。这是AT模式最大的风险点,也是TCC在金融场景更受青睐的原因。


【面试题】8. Redis分布式锁用Redisson实现时,看门狗机制是怎么工作的?为什么不建议自己用SET NX EX实现? ​

【标准答案】Redisson看门狗机制:

  1. 加锁时如果不指定leaseTime,默认锁30秒(lockWatchdogTimeout=30秒)
  2. 加锁成功后,Redisson启动一个后台线程(看门狗),每隔lockWatchdogTimeout/3(即10秒)检查一次锁是否还被持有
  3. 如果持有锁的客户端仍存活且锁key还在,看门狗自动续期到30秒
  4. 客户端宕机后,看门狗线程随之消亡,锁30秒后自动过期释放

为什么不建议自研SET NX EX:

  1. 续期问题:自研锁设置了过期时间,如果业务执行超过过期时间,锁自动释放,其他线程拿到锁,导致并发安全问题。Redisson的看门狗自动续期解决了这个问题。

  2. 释放安全:自研锁直接DEL释放,可能误删别人的锁。标准做法是LUA脚本判断value再删,但手写LUA容易出错。Redisson内部用LUA脚本保证了原子性。

  3. 可重入:自研锁不支持同一线程重入。Redisson用Hash结构存储(key → field:threadId → value:重入次数),天然支持可重入。

  4. 集群高可用:自研锁在主从切换时可能丢失锁。Redisson提供RedLock算法(多节点加锁,过半成功才算获取锁),虽然有争议,但至少提供了选项。

面试一句话:自研SET NX EX能解决80%的场景,但续期、可重入、安全释放这三道坎,每道都容易踩坑。Redisson帮你全部处理好了,生产环境没理由重复造轮子。


【面试题】9. Spring Cloud Stream如何保证消息的可靠性投递?消息积压了怎么处理? ​

【标准答案】可靠性投递保障:

  1. 生产端确认:配置spring.cloud.stream.bindings.output.producer.required-groups确保消息至少被一个消费者组接收。配合RabbitMQ的publisher confirms或Kafka的acks=all,确保消息到达Broker。

  2. 消费端手动ACK:配置spring.cloud.stream.bindings.input.consumer.acknowledge-mode=manual,业务处理完成后手动调用Acknowledgment.acknowledge()。如果处理失败不ACK,消息会重新投递。

  3. 死信队列:消费重试达到最大次数后,消息进入死信队列(DLQ),配置enableDlq: true。死信队列的消息可以人工审查后重新投递或补偿。

  4. 分区有序:配置partitionKeyExpression确保同一业务ID的消息路由到同一分区,保证局部有序。

消息积压处理:

  1. 紧急扩容:增加消费者实例数(注意不能超过分区数,多了也是闲置)
  2. 临时降级:非核心业务的消息暂停消费,腾出资源给核心业务
  3. 批量消费:将消费模式从逐条处理改为批量处理,提高吞吐
  4. 跳过+补偿:积压太久的数据如果已无实时价值,先跳过消费最新数据,历史数据走离线批处理补偿
  5. 根因排查:消费端有没有慢查询?有没有外部API超时?下游服务有没有瓶颈?

面试加分:Kafka分区数是消费并行度的上限。如果你有10个分区但只有3个消费者,最多只能3路并行。要扩容消费者先得扩分区数,但Kafka分区只能增不能减。


【面试题】10. 微服务之间调用如何安全地传递Token?网关鉴权和服务间内部调用怎么设计? ​

【标准答案】Token传递机制:

  1. 网关统一鉴权:外部请求到网关,Gateway的GlobalFilter校验JWT,验签通过后将用户信息(userId、roles等)放入HTTP Header(如X-User-Id、X-User-Roles)转发给下游服务。

  2. 内部服务间调用:OpenFeign通过RequestInterceptor拦截器,将上游请求中的Header透传到下游。核心代码:

java
@Component
public class FeignRequestInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate template) {
        ServletRequestAttributes attrs = (ServletRequestAttributes) 
            RequestContextHolder.getRequestAttributes();
        if (attrs != null) {
            HttpServletRequest request = attrs.getRequest();
            template.header("X-User-Id", request.getHeader("X-User-Id"));
            template.header("X-User-Roles", request.getHeader("X-User-Roles"));
        }
    }
}
  1. 内部信任边界:服务间调用不再校验JWT,而是信任网关传过来的Header。服务内部通过拦截器从Header提取用户信息放入ThreadLocal或SecurityContext。

安全设计要点:

  1. 网关是唯一入口:内部服务不对外暴露端口,所有外部流量必须经过网关。K8s中可用NetworkPolicy限制只有Gateway能访问内部Service。

  2. 防伪造:内部服务间信任Header的前提是这些Header只能由网关设置。可以在网关加一个内部签名Header(如X-Gateway-Signature),下游服务验证签名,防止伪造。

  3. 服务间内部调用:如果是定时任务或系统间调用(无用户上下文),使用Service Account Token,走OAuth2 Client Credentials模式获取专用Token。

  4. Token刷新:网关处处理Refresh Token逻辑,下游服务无感知。JWT快过期时,网关自动用Refresh Token换新Access Token,对下游透明。

面试关键:核心原则是"边界鉴权,内部信任"——在系统边界(网关)做严格鉴权,内部服务间减少重复校验,靠网络隔离和签名信任来保证安全。