SpringCloud微服务架构面试题库
生成时间:2026-05-21 23:18 题目数量:10道
本轮 SpringCloud微服务架构面试题库(带答案)
【面试题】1. Eureka自我保护机制是什么?什么场景下会触发?生产环境要不要关闭?
【标准答案】 Eureka自我保护机制是Eureka Server在检测到大量服务实例失联时的一种保护策略。当Eureka Server在短时间内丢失超过85%的心跳(默认阈值),就会触发自我保护模式,此时Server不再剔除已过期的服务实例。
触发场景主要是网络分区故障——比如Eureka Server和Client之间网络抖动,导致大量Client心跳无法到达Server。这时候如果Server盲目剔除所有"失联"的服务,等网络恢复后,Client重新注册会导致一段时间的服务不可用。
生产环境强烈建议不要关闭这个机制。自我保护是Eureka作为AP架构的核心体现——优先保证可用性。关闭后,网络抖动会直接导致服务被错误剔除,引发雪崩。正确的做法是:
- 调整心跳间隔和续约阈值,让它更贴合你的网络环境
- 结合健康检查接口,让网关或负载均衡器感知真实的服务状态
- 如果需要快速下线,直接调用Eureka的REST接口下线,而不是依赖心跳超时
面试加分项:可以补充一句"我们在实际项目中,会配合Eureka的actuator健康检查,让网关层面的灰度发布更精准"。
【面试题】2. Ribbon有哪些负载均衡策略?生产环境如何选择?如何自定义策略?
【标准答案】 Ribbon内置了7种负载均衡策略,最常用的是:
- RoundRobinRule:轮询,按顺序依次选择,适合请求量均匀的场景
- RandomRule:随机,适合对请求分布要求不高的场景
- WeightedResponseTimeRule:权重基于响应时间,响应快的实例权重高,适合服务实例性能差异大的场景
- BestAvailableRule:选择并发请求最少的实例,适合请求处理时间差异大的场景
- RetryRule:轮询+重试,失败后自动重试下一个实例
- AvailabilityFilteringRule:过滤掉故障实例,适合对可用性要求高的场景
- ZoneAvoidanceRule:多机房场景,优先选择同Zone实例
生产环境选择策略:
- 单机房、实例性能均匀:轮询就够用,简单稳定
- 多机房部署:ZoneAvoidanceRule,减少跨机房调用延迟
- 服务实例性能差异大(比如新老服务器混用):WeightedResponseTimeRule,让好机器多干活
- 对容错要求高:AvailabilityFilteringRule,自动过滤掉熔断的实例
自定义策略实现:继承AbstractLoadBalancerRule,重写choose方法,在里面写你的选实例逻辑。比如我们项目里做过一个"标签路由"策略——服务实例打了canary/prod标签,按请求头的版本标识路由到对应实例,实现灰度发布。
面试加分项:可以提一句"Spring Cloud LoadBalancer是Ribbon的替代方案,官方推荐新项目用LoadBalancer,支持Reactive编程模型"。
【面试题】3. OpenFeign调用超时怎么配置?超时时间和重试次数如何权衡?踩过什么坑?
【标准答案】 OpenFeign超时配置分两层:
连接超时(connectTimeout):建立TCP连接的超时时间,一般设1-2秒 读取超时(readTimeout):等待响应数据的超时时间,根据业务接口响应时间设置
配置方式(yaml):
feign:
client:
config:
default:
connectTimeout: 2000
readTimeout: 5000
user-service:
connectTimeout: 1000
readTimeout: 3000超时和重试的权衡:
- 超时时间短 + 重试次数多:适合幂等接口,能快速失败并尝试其他实例,但可能增加服务压力
- 超时时间长 + 不重试或少重试:适合非幂等接口,避免重复执行导致数据问题
踩过的坑:
- 超时时间没区分接口类型:查询接口设1秒超时OK,但导出报表接口也要跑10秒,结果频繁超时。解决方案:按接口粒度配置,复杂接口单独设置长超时
- 重试导致数据重复:支付接口超时后重试,用户扣了两次钱。解决方案:支付类非幂等接口
ribbon.retryableStatusCodes只配置5xx,或者直接关闭重试,用本地重试表+异步补偿 - 超时时间层层叠加:网关→服务A→服务B,每层都设5秒,用户等15秒才超时。解决方案:调用链路上,下游服务超时时间要小于上游,比如网关6秒、A服务4秒、B服务2秒
面试加分项:"我们现在的规范是:接口文档里必须标注P95响应时间,Feign超时时间按P95的1.5倍配置,重试只对GET请求开启"。
【面试题】4. Sentinel熔断状态机的三个状态是什么?状态之间如何转换?半开状态的作用?
【标准答案】 Sentinel熔断状态机有三个状态:关闭(Closed)、开启(Open)、半开(Half-Open)。
状态转换逻辑:
- 关闭→开启:当请求错误率(异常比例/慢调用比例)超过阈值,并且最小请求数达到触发条件,熔断器打开
- 开启→半开:熔断器开启后,经过熔断时长(比如10秒),自动进入半开状态
- 半开→关闭或开启:
- 半开状态下,放行一个请求探测
- 如果探测成功,熔断器关闭,恢复正常调用
- 如果探测失败,熔断器重新打开,继续熔断
半开状态的作用: 半开状态是熔断恢复的关键机制。它不会一直熔断下去,而是用一个探测请求"试探"服务是否恢复。如果恢复了就恢复调用,没恢复就继续熔断。
实际配置建议:
- 熔断时长不要设太短(至少10秒),给下游服务恢复时间
- 探测请求的超时时间要短,快速判断服务是否恢复
- 生产环境建议用慢调用比例熔断(RT熔断),比异常比例熔断更早发现服务变慢
面试加分项:"我们线上遇到过一个问题:服务只是偶尔变慢,但没挂,这时候异常比例熔断触发不了,但慢调用比例熔断就能生效,保护上游不被拖慢"。
【面试题】5. Spring Cloud Gateway如何实现灰度发布?路由断言和过滤器如何配合?
【标准答案】 灰度发布的核心思路:根据请求特征(请求头、Cookie、参数等)路由到不同版本的服务实例。
实现步骤:
第一步:服务实例打标签 服务启动时,通过环境变量或配置中心给实例打标签:
spring:
cloud:
nacos:
discovery:
metadata:
version: v2 # 灰度版本第二步:Gateway配置灰度路由
spring:
cloud:
gateway:
routes:
- id: user-service-v1
uri: lb://user-service
predicates:
- Path=/api/user/**
- Header=X-Version, v1
metadata:
version: v1
- id: user-service-v2
uri: lb://user-service
predicates:
- Path=/api/user/**
- Header=X-Version, v2
metadata:
version: v2第三步:自定义负载均衡器 继承ReactorServiceInstanceLoadBalancer,根据请求头的版本标识,过滤出对应标签的服务实例。
路由断言和过滤器配合:
- 断言(Predicate):决定请求走哪条路由规则,比如匹配请求头、路径、Cookie
- 过滤器(Filter):在请求转发前后做加工,比如加请求头、鉴权、限流
灰度发布典型流程:
- 断言匹配请求头的版本号,决定走v1或v2路由
- 过滤器链中,先做鉴权,再加灰度标识到请求头(传递给下游)
- 负载均衡器根据灰度标识,选择对应版本的服务实例
面试加分项:"我们项目里还做过按用户ID灰度的方案,在过滤器里把用户ID哈希,尾号0-9走新版本,其他走旧版本,实现渐进式灰度"。
【面试题】6. Nacos配置中心如何实现配置热更新?ConfigListener机制原理是什么?
【标准答案】 Nacos配置热更新有两种方式:
方式一:@RefreshScope注解 在配置类上加@RefreshScope,配置变更后,Bean会重新创建,注入最新配置值:
@RefreshScope
@RestController
public class TestController {
@Value("${app.config}")
private String config;
}方式二:ConfigListener监听 手动添加监听器,配置变更时触发回调:
nacosConfigManager.getConfigService().addListener("app-config", "DEFAULT_GROUP", new Listener() {
@Override
public void receiveConfigInfo(String configInfo) {
// 处理新配置
}
});ConfigListener机制原理:
- 客户端启动时,与Nacos Server建立长轮询连接(HTTP长连接)
- 服务端配置变更时,通过长轮询主动推送变更通知给客户端
- 客户端收到通知后,拉取最新配置,触发Listener回调
关键点:
- 长轮询默认30秒超时,服务端在超时前有变更就立即返回,没有变更就等到超时
- 配置变更延迟通常在秒级,比主动轮询效率高
- 客户端本地有配置快照,Nacos Server挂了也能用本地缓存
生产环境注意事项:
- 配置变更要做版本管理,配置中心开启配置历史,方便回滚
- 敏感配置(数据库密码)要加密存储,用Nacos的配置加密功能
- 灰度发布时,先更新灰度实例的配置,验证无误再全量推送
面试加分项:"我们项目里把配置变更事件接入了审计日志,谁改了什么配置、什么时间改的,都有记录,出问题能快速回滚"。
【面试题】7. Zipkin链路追踪对性能有什么影响?采样率如何配置?生产环境如何降低性能损耗?
【标准答案】 Zipkin链路追踪的性能影响主要来自三个方面:
1. 数据采集开销 每次调用都要生成TraceId/SpanId,记录时间戳、标签等信息,占用CPU和内存
2. 数据传输开销 Span数据通过网络发送到Zipkin Server,网络IO和序列化都有开销
3. 存储开销 Zipkin Server存储海量Span数据,存储压力大
采样率配置:
- 采样率(sampler.probability):0.0~1.0,表示多大比例的请求会被追踪
- 开发环境:1.0(100%采样),方便调试
- 生产环境:0.01~0.1(1%~10%采样),根据QPS调整
配置方式(yaml):
spring:
sleuth:
sampler:
probability: 0.1 # 10%采样率降低性能损耗的方案:
- 合理设置采样率:高QPS服务降低采样率(1%),低QPS服务可以提高(10%~50%)
- 异步发送Span数据:用异步Reporter,避免阻塞业务线程
- 使用更高效的存储:Zipkin + Elasticsearch,比MySQL性能好
- 按需开启:只对核心链路开启追踪,边缘服务可以不接
- 使用更轻量的追踪方案:Micrometer Tracing + Prometheus,只记录指标不记录完整Span
生产环境建议:
- QPS>1000的服务,采样率建议1%或更低
- 错误请求强制采样(sampler额外配置),方便排查问题
- 接口响应时间包含追踪耗时,做性能分析时注意区分
面试加分项:"我们线上做过压测,全量采样时QPS下降约5%,改成1%采样后影响可以忽略不计。错误请求我们会强制采样,这样既能降低开销,又能保证问题排查能力"。
【面试题】8. Seata的AT模式、TCC模式、Saga模式有什么区别?分别适合什么场景?
【标准答案】 Seata四种分布式事务模式的对比:
| 模式 | 核心原理 | 一致性 | 性能 | 复杂度 | 适用场景 |
|---|---|---|---|---|---|
| AT | 自动补偿,基于undo_log回滚 | 最终一致 | 高 | 低 | 大部分业务场景 |
| TCC | 业务手动实现Try-Confirm-Cancel | 强一致 | 中 | 高 | 对一致性要求高的核心业务 |
| Saga | 长事务编排,正向补偿 | 最终一致 | 中 | 中 | 跨多个服务的长流程业务 |
| XA | 数据库XA协议,两阶段提交 | 强一致 | 低 | 低 | 传统数据库,跨库不跨服务 |
AT模式:
- 最简单,业务代码无侵入
- Seata自动生成undo_log,失败时自动回滚
- 局限性:依赖数据库事务,不支持多SQL混合操作,可能脏写
TCC模式:
- 业务代码要写三个方法:Try(预留资源)、Confirm(提交)、Cancel(回滚)
- 一致性最高,Try成功后资源已锁定
- 局限性:业务侵入性强,开发成本高
Saga模式:
- 适合长流程业务,比如"下单→支付→发货→通知"
- 每个步骤有正向操作和补偿操作,失败时逆向补偿
- 局限性:没有隔离性,可能出现中间状态被其他事务看到
生产环境选择建议:
- 90%场景用AT:简单、开发快,只要业务能接受最终一致性
- 核心资金类用TCC:支付、账户余额,必须强一致
- 跨天长流程用Saga:比如"用户注册→填写资料→审核→开通",跨度几小时甚至几天
面试加分项:"我们项目的支付模块用的是TCC,Try阶段冻结余额,Confirm扣款,Cancel解冻;其他模块全用AT,开发效率高很多"。
【面试题】9. Redis分布式锁用SET NX EX实现有什么问题?Redisson如何解决锁续期问题?RedLock是什么?
【标准答案】 原生SET NX EX的问题:
- 锁过期问题:业务执行时间超过锁过期时间,锁自动释放,其他线程拿到锁,导致并发问题
- 锁误删问题:线程A的业务执行完,去删锁,但这时候锁已经被线程B持有,A把B的锁删了
- 单点故障:Redis主节点挂了,锁丢失
Redisson的解决方案:
锁续期问题: Redisson用Watchdog机制解决。原理:
- 锁默认过期时间30秒
- Redisson启动一个定时任务,每10秒(过期时间的1/3)检查一次锁是否还持有
- 如果持有,自动续期到30秒
- 业务执行完,释放锁时会停止Watchdog
锁误删问题: Redisson的锁用Lua脚本保证原子性:
- 加锁时写入
key和线程标识(threadId) - 解锁时校验线程标识,只有自己加的锁才能释放
RedLock是什么: RedLock(Redlock algorithm)是Redis官方提出的分布式锁算法,解决单点故障问题。
原理:同时向多个Redis节点(通常5个)申请锁,只有超过半数节点加锁成功,才算加锁成功。
实现步骤:
- 获取当前时间戳
- 依次向N个Redis节点申请锁
- 计算加锁耗时,只有耗时小于锁过期时间,才算成功
- 成功加锁的节点数 > N/2,才算真正成功
- 失败时,向所有节点发送解锁命令
RedLock的争议:
- 在极端情况下(网络分区、时钟跳跃)可能不安全
- 实际生产中,对一致性要求极高的场景建议用Zookeeper分布式锁
面试加分项:"我们线上用的是Redisson,Watchdog续期完全够用;对一致性要求最高的支付模块,我们用的Zookeeper锁,CP架构更可靠"。
【面试题】10. Spring Cloud Stream消息积压怎么处理?消费者如何实现负载均衡?
【标准答案】 消息积压的原因:
- 消费者处理速度慢,生产者发消息速度 > 消费者处理速度
- 消费者服务挂了,消息堆积在队列
- 消费者实例少,处理能力不足
解决方案:
1. 增加消费者实例
- 最直接的方式,横向扩展消费者数量
- 注意:实例数不能超过分区数,否则多出来的实例不消费
2. 批量消费
- 配置批量消费,一次拉取多条消息处理
- 减少网络IO次数,提升吞吐
spring:
cloud:
stream:
bindings:
input:
consumer:
batch-size: 10 # 一次拉取10条3. 异步处理
- 消费者收到消息后,异步处理,不阻塞消费线程
- 用线程池或异步框架,提升处理并发
4. 死信队列
- 处理失败的消息,放到死信队列,避免阻塞正常消费
- 离线人工处理或定时重试
消费者负载均衡实现: Spring Cloud Stream通过分区(Partition)机制实现消费者负载均衡:
spring:
cloud:
stream:
bindings:
output:
producer:
partition-key-expression: payload.id # 分区键
partition-count: 3 # 分区数
input:
consumer:
partitioned: true
instance-count: 3 # 消费者实例数
instance-index: 0 # 当前实例索引原理:
- 生产者根据分区键(比如订单ID)计算消息应该发到哪个分区
- 同一分区的消息只能被一个消费者实例处理
- 保证同一订单的消息顺序性
实际项目经验:
- 订单消息:用订单ID做分区键,保证同一订单的消息顺序
- 日志消息:不需要顺序,轮询消费就行
- 积压严重时:临时扩容消费者实例,同时把历史消息导入离线分析系统处理
面试加分项:"我们遇到过一次消息积压,原因是消费者有个慢SQL,改了索引后处理速度提升10倍,积压自动消除。所以排查积压问题,先看消费者日志和监控,找出瓶颈"。
总结
本批题目覆盖考点:
- 服务注册与发现:Eureka自我保护机制
- 负载均衡:Ribbon策略选择与自定义
- 服务调用:OpenFeign超时重试配置
- 熔断限流降级:Sentinel熔断状态机
- API网关:Gateway灰度发布实现
- 配置中心:Nacos配置热更新
- 链路追踪:Zipkin性能影响与采样率
- 分布式事务:Seata AT/TCC/Saga模式对比
- 分布式锁:Redis锁续期与RedLock
- 消息队列集成:Spring Cloud Stream消息积压处理
建议配合实战项目经验回答,面试通过率更高。