SpringCloud微服务架构面试题库
生成时间:2026-05-21 22:45 题目数量:10道
本轮 SpringCloud微服务架构面试题库(带答案)
【面试题】1. Eureka的自我保护机制是什么?什么情况下会触发?生产环境如何处理?
【标准答案】自我保护机制是Eureka Server在短时间内丢失大量客户端心跳时,不再剔除服务实例的保护措施。触发条件是当每分钟心跳续租数低于阈值(默认85%)时自动开启。这样设计的目的是防止网络分区时,健康服务被错误剔除。
生产环境的处理建议:
- 开启自我保护:
eureka.server.enable-self-preservation=true(默认开启) - 合理配置心跳参数:心跳间隔30秒,续租过期时间90秒
- 配置多节点Eureka集群,避免单点故障
- 监控Eureka Server的健康状态,关注
Renews threshold和Renews (last min)指标 - 如果确定服务已下线,可临时关闭自我保护清理实例
核心思想:宁可保留错误的服务实例,也不剔除健康的实例,配合集群部署保证高可用。
【面试题】2. Ribbon和Spring Cloud LoadBalancer有什么区别?为什么推荐使用后者?
【标准答案】核心区别在于:
Ribbon:
- Netflix开源,已进入维护模式(停止迭代)
- 功能完善,支持多种负载均衡策略
- 与Spring Cloud深度集成多年,生态成熟
- 但已不推荐新项目使用
Spring Cloud LoadBalancer:
- Spring官方替代方案,持续维护
- 更轻量,移除了Ribbon的冗余功能
- 支持Reactive编程模型(WebFlux友好)
- 配置更简洁,与Spring Boot自动配置无缝集成
- 支持自定义负载均衡策略,实现
ReactorServiceInstanceLoadBalancer接口
推荐后者的原因:
- Ribbon已停止维护,存在潜在Bug风险
- LoadBalancer是Spring官方长期支持方案
- 更好的云原生和响应式支持
- Spring Cloud 2020+版本已默认使用LoadBalancer
迁移成本很低,主要是依赖替换和少量配置调整。
【面试题】3. OpenFeign的超时重试机制如何配置?如何避免重试导致的重复下单问题?
【标准答案】超时配置:
feign:
client:
config:
default:
connectTimeout: 5000 # 连接超时5秒
readTimeout: 10000 # 读取超时10秒
userService: # 针对特定服务
connectTimeout: 3000
readTimeout: 5000重试配置: 默认情况下Feign会重试,可通过自定义Retryer控制:
@Configuration
public class FeignConfig {
@Bean
public Retryer retryer() {
// 最大重试次数5,初始间隔100ms,最大间隔1s
return new Retryer.Default(100, 1000, 5);
// 或者完全禁止重试
// return Retryer.NEVER_RETRY;
}
}避免重复下单的关键策略:
- 接口幂等性设计:唯一请求ID(Idempotency-Key),服务端去重
- 关闭GET请求重试:GET可重试,POST/PUT/PATCH建议关闭
- 分布式锁:关键操作前先获取锁
- Token机制:下单前获取Token,服务端消费后失效
- 数据库唯一约束:订单号唯一,重复插入失败
最佳实践:非幂等操作关闭重试,配合业务层幂等性设计双重保障。
【面试题】4. Sentinel的熔断状态机三个状态之间如何流转?各状态的作用是什么?
【标准答案】Sentinel熔断状态机包含三个状态:
1. CLOSED(关闭状态)
- 初始状态,请求正常通过
- 统计请求的成功率、响应时间、异常比例
- 当指标达到阈值时,切换到OPEN状态
2. OPEN(打开状态)
- 熔断生效,所有请求直接触发fallback
- 设置熔断时长(如10秒),期间不检查后端服务
- 熔断时长结束后,进入HALF-OPEN状态
3. HALF-OPEN(半开状态)
- 探测状态,允许少量请求(如1个)通过
- 如果请求成功:说明服务恢复,切换到CLOSED
- 如果请求失败:说明服务仍异常,重新回到OPEN
流转完整流程:
CLOSED --(异常比例/慢调用比例达阈值)--> OPEN
OPEN --(熔断时长结束)--> HALF-OPEN
HALF-OPEN --(探测成功)--> CLOSED
HALF-OPEN --(探测失败)--> OPEN三种熔断策略:
- 慢调用比例:响应时间超过阈值RT的比例
- 异常比例:异常数占总请求的比例
- 异常数:一分钟内的异常数量
核心思想:快速失败,保护系统,定时探测恢复。
【面试题】5. Spring Cloud Gateway如何实现灰度发布?具体的路由策略有哪些?
【标准答案】核心实现方式:
1. 基于权重的路由策略
spring:
cloud:
gateway:
routes:
- id: user-service-v1
uri: lb://USER-SERVICE-V1
predicates:
- Weight=user-group, 80 # 80%流量到v1
- id: user-service-v2
uri: lb://USER-SERVICE-V2
predicates:
- Weight=user-group, 20 # 20%流量到v22. 基于请求头的路由
predicates:
- Header=X-Gray-Version, v2适用场景:内部测试、特定用户群体
3. 基于Cookie的路由
predicates:
- Cookie=gray-user, true适用场景:白名单用户灰度
4. 基于参数的路由
predicates:
- Query=version, v2适用场景:通过URL参数控制版本
5. 自定义灰度路由策略 实现RouteLocator接口,根据业务规则动态路由:
public class GrayRouteLocator implements RouteLocator {
// 从配置中心/Nacos读取灰度规则
// 根据用户ID、地区、设备类型等维度路由
}完整灰度发布流程:
- 新版本服务注册到Nacos(带上版本标签)
- 网关配置灰度路由规则(从小流量开始)
- 监控新版本指标(错误率、响应时间)
- 逐步扩大流量比例
- 全量切换,下线旧版本
关键点:配合Nacos元数据、Sentinel流量控制、完善的监控告警。
【面试题】6. Nacos Config的配置热更新是如何实现的?ConfigListener的工作原理是什么?
【标准答案】热更新实现机制:
1. 客户端长轮询
- Nacos Client定时向Server发起长轮询(默认30秒)
- Server挂起请求,等待配置变更
- 配置变化时立即返回,无变化则超时返回304
2. 配置缓存
- Client本地缓存配置文件
- 启动时先读本地缓存,再异步拉取最新配置
- 保证启动速度和网络容错
3. 配置版本对比
- 每次配置带MD5校验
- Client发现MD5变化时触发更新
ConfigListener工作原理:
@NacosConfigListener(dataId = "application.yml", groupId = "DEFAULT_GROUP")
public void onConfigChange(String newConfig) {
// 配置变更回调
}内部流程:
- Client注册Listener到本地ListenerManager
- 长轮询线程检测到配置变化
- 计算新配置MD5与本地不一致
- 触发所有注册的Listener回调
- 执行业务自定义逻辑
与@RefreshScope配合:
@RestController
@RefreshScope // 配置变更时Bean重建
public class ConfigController {
@Value("${custom.config}")
private String config;
}工作原理:
- Nacos配置变化 → 触发Environment更新
@RefreshScope的Bean被标记为脏- 下次访问时重新创建Bean,注入新值
关键点:长轮询保证实时性,本地缓存保证可用性。
【面试题】7. Sleuth如何实现TraceId在微服务调用链中的传递?异步场景下如何保证链路完整?
【标准答案】同步调用链传递机制:
1. ThreadLocal存储
- TraceId和SpanId存储在
ThreadLocal中 - 当前线程共享同一个Trace上下文
2. HTTP Header传递
- 服务A发起请求时,Sleuth拦截器自动添加Header:
X-B3-TraceId:全局唯一追踪IDX-B3-SpanId:当前Span IDX-B3-ParentSpanId:父Span IDX-B3-Sampled:是否采样
3. 服务B接收
- Sleuth Filter解析Header
- 创建新Span,关联父Span
- 继续传递给下游服务
异步场景的解决方案:
1. @Async异步方法
@Bean
public TaskExecutor taskExecutor() {
return new DelegatingSecurityContextExecutor(
new ThreadPoolTaskExecutor(),
new LazyTraceExecutor(tracer, executor)
);
}2. 线程池手动传递
@Autowired
private Tracer tracer;
public void asyncMethod() {
ExecutorService executor = Executors.newFixedThreadPool(5);
Runnable task = tracer.withSpanInScope(
tracer.currentSpan(),
() -> { /* 业务逻辑 */ }
);
executor.submit(task);
}3. 消息队列场景
- Spring Cloud Stream自动集成Sleuth
- TraceId注入到消息Header
- 消费者自动提取并续链
关键原理:
- 同步调用:ThreadLocal + HTTP Header自动传递
- 异步调用:手动包装Runnable/Callable,传递ThreadLocal上下文
- 消息队列:消息Header作为载体
核心思想:每个调用边界都进行Trace上下文的序列化与反序列化。
【面试题】8. Seata的AT模式和TCC模式有什么本质区别?各自的适用场景是什么?
【标准答案】本质区别对比:
| 维度 | AT模式 | TCC模式 |
|---|---|---|
| 侵入性 | 无侵入,自动补偿 | 高侵入,需编写Try/Confirm/Cancel |
| 实现复杂度 | 低,依赖undo_log | 高,需处理幂等、空回滚、悬挂 |
| 性能 | 较好,锁粒度小 | 高,无全局锁,资源预留 |
| 一致性 | 最终一致,依赖数据库 | 强一致,业务控制 |
| 适用场景 | 传统SQL应用 | 高并发、非数据库资源 |
| 限制 | 仅支持数据库事务 | 任意资源(Redis、MQ等) |
AT模式原理:
- 解析SQL,记录修改前镜像(before image)
- 执行业务SQL
- 记录修改后镜像(after image)
- 生成undo_log插入数据库
- 全局提交:删除undo_log
- 全局回滚:根据undo_log反向生成SQL恢复数据
TCC模式原理:
- Try:资源预留(冻结库存)
- Confirm:确认提交(扣减库存)
- Cancel:取消释放(解冻库存)
TCC三大问题解决方案:
1. 空回滚(Try未执行,先执行Cancel)
// Cancel时检查Try是否执行过
if (tryExecuted.get() == null) {
// 记录空回滚标记,直接返回
return;
}2. 幂等性(重复Confirm/Cancel)
// 状态机控制,已处理直接返回
if (status == CONFIRMED || status == CANCELED) {
return;
}3. 悬挂(Cancel先于Try执行)
// Try时检查是否已有空回滚标记
if (hasEmptyRollbackLog()) {
return;
}适用场景建议:
- AT模式:95%的常规业务,对代码无侵入要求
- TCC模式:库存扣减、账户余额、高并发金融场景
- Saga模式:长事务、编排型业务流程
【面试题】9. Redis分布式锁的Redisson看门狗(Watchdog)机制是什么?为什么需要锁续期?
【标准答案】为什么需要锁续期:
问题背景:
SET lock:key value NX EX 30 # 加锁,30秒过期如果业务执行超过30秒:
- 锁已自动过期释放
- 其他线程获得锁
- 原线程执行完毕释放锁(可能释放别人的锁)
- 导致并发安全问题
Watchdog机制原理:
Redisson的看门狗是一个后台定时任务:
- 默认锁过期时间30秒(
lockWatchdogTimeout) - 每隔10秒(1/3过期时间)检查锁是否还持有
- 如果持有,自动续期到30秒
- 业务执行完毕,主动释放锁,看门狗停止
核心代码:
RLock lock = redisson.getLock("myLock");
lock.lock(); // 启动Watchdog
try {
// 业务执行超过30秒也安全
Thread.sleep(60000);
} finally {
lock.unlock(); // 释放锁,停止Watchdog
}手动指定leaseTime时Watchdog不生效:
lock.lock(10, TimeUnit.SECONDS); // 不会自动续期Watchdog的优势:
- 自动续期,无需预估业务执行时间
- 避免业务未完成锁过期
- 防止死锁(客户端宕机,锁会在过期时间后自动释放)
注意事项:
- 需要Redisson客户端,原生Redis不支持
- 看门狗是客户端任务,Redis Server重启会导致锁丢失
- 集群模式下推荐使用RedLock算法
核心思想:让锁的生命周期与业务执行时间解耦,自动管理锁续期。
【面试题】10. Spring Cloud Stream的消息分组(Consumer Group)如何保证消息只被消费一次?
【标准答案】问题背景:
微服务多实例部署时,同一条消息可能被多个实例重复消费:
订单服务实例1 → 收到消息
订单服务实例2 → 收到消息(重复!)Consumer Group解决方案:
1. 配置消息分组
spring:
cloud:
stream:
bindings:
orderInput:
destination: order-topic
group: order-group # 关键配置2. 工作原理:
- 同一个group内的多个消费者实例共享订阅
- 消息队列(如RabbitMQ/Kafka)保证同一group只投递给一个实例
- 不同group各自独立消费(广播语义)
3. 不同MQ的实现差异:
RabbitMQ:
- 创建队列命名:
order-topic.order-group - 多实例绑定同一队列,竞争消费
- 消息轮询投递给各实例
Kafka:
- Consumer Group机制,分区分配策略
- 同一组内,一个分区只能被一个消费者消费
- 分区数决定并行度
RocketMQ:
- Consumer Group机制
- 同一组内消息负载均衡
核心配置示例:
spring:
cloud:
stream:
function:
definition: consumeOrder
bindings:
consumeOrder-in-0:
destination: order-topic
group: order-service-group # 保证单实例消费
consumer:
max-attempts: 3 # 重试次数
rocketmq:
bindings:
consumeOrder-in-0:
consumer:
orderly: true # 顺序消费补充保证机制:
- 幂等性设计:业务层去重(消息ID、业务唯一键)
- ACK机制:消费成功才确认,失败则重试
- 死信队列:重试失败转入DLQ,人工处理
核心思想:消息分组将竞争消费者模式变为负载均衡模式,保证消息精准投递。
总结
本套面试题涵盖:
- 服务注册发现(Eureka自我保护)
- 负载均衡(Ribbon vs LoadBalancer)
- 服务调用(OpenFeign超时重试)
- 熔断限流(Sentinel状态机)
- API网关(Gateway灰度发布)
- 配置中心(Nacos热更新)
- 链路追踪(Sleuth TraceId传递)
- 分布式事务(Seata AT/TCC)
- 分布式锁(Redisson看门狗)
- 消息队列(Stream消息分组)
建议复习重点:
- 核心原理必须理解透彻
- 生产环境最佳实践
- 故障场景应对策略
- 与面试官互动的实战经验表达