本轮 SpringCloud微服务架构面试题库(带答案)
【面试题】1. Eureka自我保护机制是什么?生产环境要不要关闭?
【标准答案】自我保护机制是Eureka Server在短时间内丢失大量客户端心跳时,认为是网络故障而非服务宕机,于是停止剔除失效实例,保护注册表。
具体原理:当每分钟心跳数低于阈值(默认每分钟应收到心跳数 * 0.85)时触发,Eureka不再从注册表移除任何实例,即使客户端长时间不续约也保留。
生产环境建议:不要关闭。原因有三:
- 网络波动是常态,自我保护可防止因网络抖动导致服务被误剔除
- AP模型的设计初衷就是保证可用性,宁可保留过期实例也不能丢失正常实例
- 服务消费者有重试机制和熔断保护,即使调用到失效实例也能优雅降级
如果确实需要剔除,可调低阈值(eureka.server.renewalPercentThreshold)而非直接关闭。
【面试题】2. Nacos作为注册中心,AP模式和CP模式怎么选?
【标准答案】Nacos支持AP和CP两种模式切换,核心选择标准是:对数据一致性的敏感程度。
选AP模式(默认):
- 场景:临时服务实例,允许短暂数据不一致
- 原因:服务注册场景下,可用性比一致性更重要。网络分区时,至少能继续提供服务发现能力
- 特点:使用Distro协议,各节点平等、最终一致,性能更高
选CP模式:
- 场景:持久化服务实例,或对服务实例数据强一致要求高
- 原因:持久实例必须持久化到数据库,需要Raft协议保证强一致
- 特点:使用Raft协议,选举Leader,数据一致但可用性略降(节点过半不可用则服务不可用)
切换方式:配置 nacos.naming.data.mode=cp 或调用API临时切换。
实践经验:90%场景用AP,只有需要持久化实例或强一致要求的业务才切CP。
【面试题】3. Ribbon和Spring Cloud LoadBalancer有什么区别?
【标准答案】两者都是客户端负载均衡组件,但有本质区别:
Ribbon:
- 属于Netflix全家桶,已进入维护模式
- 支持多种负载均衡策略:轮询、随机、加权响应时间等
- 可通过配置文件自定义规则
- 与Eureka深度集成,自动维护服务列表
Spring Cloud LoadBalancer:
- Spring官方推出,用于替代Ribbon
- 基于Reactive编程模型(WebFlux)
- 默认只有轮询和随机两种策略,扩展更简洁
- 与Spring Cloud生态深度整合,支持服务发现自动发现
核心差异:
- 编程模型:Ribbon是阻塞式,LoadBalancer支持响应式
- 维护状态:Ribbon停止维护,LoadBalancer是官方推荐
- 扩展性:LoadBalancer通过ServiceInstanceListSupplier扩展更灵活
迁移建议:新项目直接用LoadBalancer,老项目可继续用Ribbon但逐步迁移。
【面试题】4. OpenFeign的超时重试怎么配置?有哪些坑?
【标准答案】OpenFeign有两个超时参数:
feign:
client:
config:
default:
connectTimeout: 5000 # 连接超时
readTimeout: 10000 # 读取超时重试机制:默认使用Retryer.NEVER_RETRY,即不重试。需自定义Retryer:
@Bean
public Retryer retryer() {
return new Retryer.Default(100, 1000, 3); // 初始间隔100ms,最大1s,最多3次
}常见坑点:
- 超时时间设置过短:没考虑网络波动,导致频繁超时重试
- 重试与业务幂等冲突:非幂等接口重试可能产生重复数据
- 全局配置覆盖局部:default配置会被特定服务配置覆盖,需注意优先级
- 超时叠加:Feign超时 + Ribbon超时 + Hystrix/Sentinel超时,需整体规划
最佳实践:
- 连接超时建议2-5秒
- 读超时根据业务P99响应时间设置,一般5-30秒
- 写操作慎用重试,或确保幂等性
- 监控超时指标,动态调整阈值
【面试题】5. Sentinel的熔断降级有哪几种策略?分别适用什么场景?
【标准答案】Sentinel提供三种熔断策略:
1. 慢调用比例:
- 原理:统计慢调用比例,超过阈值触发熔断
- 适用:对外部依赖响应时间敏感的场景,如调用第三方API
- 配置示例:慢调用RT > 500ms,比例 > 50%,触发熔断
2. 异常比例:
- 原理:统计异常请求比例,超过阈值触发熔断
- 适用:对异常敏感但响应时间要求不高的场景
- 配置示例:异常比例 > 30%,触发熔断
3. 异常数:
- 原理:统计异常数量,超过阈值触发熔断
- 适用:流量较小的服务,避免偶发异常误触发
- 配置示例:1分钟内异常数 > 10,触发熔断
熔断状态机:关闭 → 开启 → 半开启
- 关闭:正常请求
- 开启:熔断生效,直接返回降级结果
- 半开启:放行一个请求探测,成功则关闭,失败则重新开启
实践建议:高并发场景用慢调用比例,低流量用异常数,外部依赖用异常比例。
【面试题】6. Spring Cloud Gateway如何实现动态路由?
【标准答案】Gateway默认从配置文件读取路由规则,但生产环境需要动态更新。
实现方案:
方案一:基于Nacos配置中心
@Component
public class NacosDynamicRouteService {
@NacosValue(value = "${gateway.routes}", autoRefreshed = true)
private String routesConfig;
// 监听配置变化,更新RouteDefinitionLocator
}方案二:基于数据库
- 将路由规则存储到数据库
- 实现RouteDefinitionLocator接口,从DB读取
- 配合缓存提高性能
方案三:基于Redis
- 利用Gateway的RouteDefinitionRepository
- 监听Redis变更,实时推送路由更新
核心原理:
- 实现RouteDefinitionLocator接口
- 返回Flux<RouteDefinition>,支持响应式更新
- Gateway内部通过CachingRouteDefinitionLocator缓存路由
动态刷新API:
@Autowired
private RouteDefinitionLocator routeDefinitionLocator;
public void refreshRoutes() {
// 清除缓存,重新加载路由
routeDefinitionLocator.getRouteDefinitions().subscribe();
}【面试题】7. 分布式事务Seata的AT模式原理是什么?有什么缺陷?
【标准答案】AT模式是Seata默认的分布式事务模式,全称"Auto Transaction",核心是自动补偿。
工作原理:
- 一阶段:执行业务SQL,同时记录undo_log(前镜像+后镜像)
- 二阶段提交:删除undo_log
- 二阶段回滚:根据undo_log反向生成SQL,恢复数据
关键组件:
- TC(Transaction Coordinator):事务协调者,维护全局事务状态
- TM(Transaction Manager):事务管理者,发起全局事务
- RM(Resource Manager):资源管理者,管理分支事务
缺陷与限制:
- 脏写风险:全局事务未提交时,被本地事务修改会导致数据不一致
- 解决:使用全局锁,但影响性能
- 性能开销:需要写undo_log,增加IO和存储
- SQL限制:不支持复杂SQL(如多表关联、存储过程)
- 隔离级别:默认读未提交,可能出现脏读
- 大事务问题:长事务占用连接资源,需拆分
适用场景:对一致性要求不是极高、事务较短的微服务场景。
【面试题】8. Redis分布式锁用SET NX EX有什么问题?Redisson怎么解决?
【标准答案】SET NX EX是Redis实现分布式锁的简单方式:
SET key value NX EX 30存在问题:
- 锁超时释放:业务执行时间超过锁过期时间,锁被自动释放,其他线程获得锁
- 误删锁:线程A的锁过期被删,线程B获得锁,线程A执行完删了B的锁
- 不可重入:同一线程多次获取同一把锁会失败
- 单点故障:Redis主节点宕机,锁数据丢失
Redisson解决方案:
- 看门狗机制:后台线程定期续期,默认每10秒续期30秒,防止业务未完成锁过期
- Lua脚本保证原子性:加锁和删锁用Lua脚本,避免误删
- 可重入锁:用Hash结构存储,value记录重入次数
- RedLock算法:多Redis实例,半数以上加锁成功才算成功
使用示例:
RLock lock = redisson.getLock("myLock");
lock.lock(); // 自动续期
try {
// 业务逻辑
} finally {
lock.unlock();
}【面试题】9. Spring Cloud Stream如何保证消息可靠性投递?
【标准答案】消息可靠性涉及三个阶段:发送端、中间件、消费端。
发送端保障:
spring:
cloud:
stream:
bindings:
output:
producer:
required-groups: group1 # 确保消息持久化配置说明:
required-groups:指定消费者组,确保消息持久化到队列- 开启事务消息(RocketMQ/Kafka支持)
中间件保障:
- 刷盘机制:同步刷盘保证不丢消息,异步刷盘提高性能
- 主从同步:同步复制保证高可用,异步复制提高吞吐
消费端保障:
@StreamListener(Sink.INPUT)
public void handle(Message<String> message) {
try {
process(message);
// 手动ACK
} catch (Exception e) {
// 不ACK,触发重试
throw e;
}
}配置重试策略:
spring:
cloud:
stream:
bindings:
input:
consumer:
max-attempts: 3 # 重试次数
back-off-initial-interval: 1000
back-off-multiplier: 2.0死信队列:超过重试次数后进入DLQ,需单独处理。
最佳实践:发送端确认+消费端手动ACK+死信队列兜底。
【面试题】10. 微服务架构下如何实现灰度发布?
【标准答案】灰度发布核心是按比例或按条件分流,逐步验证新版本。
实现方案:
1. 基于Gateway路由:
spring:
cloud:
gateway:
routes:
- id: service-v1
uri: lb://SERVICE-V1
predicates:
- Header=version, v1
- id: service-v2
uri: lb://SERVICE-V2
predicates:
- Header=version, v22. 基于Nacos元数据:
- 服务实例注册时带上metadata(version=v2)
- Ribbon根据元数据过滤实例
- 结合ThreadLocal传递版本标识
3. 专业方案:Istio Service Mesh:
- VirtualService配置流量比例
- DestinationRule定义服务子集
- 支持基于Header、Cookie、权重的精细分流
灰度策略:
- 金丝雀发布:先让5%流量到新版本,逐步扩大
- 蓝绿部署:新版本独立部署,一键切换
- A/B测试:按用户特征分流,对比效果
关键点:
- 服务间调用链路需传递版本标识(Header透传)
- 数据库兼容:新版本向后兼容旧版本数据
- 回滚机制:新版本异常可快速切回旧版本
- 监控告警:灰度期间密切监控错误率和性能指标