Skip to content

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

【面试题】1. Eureka自我保护机制是什么?生产环境要不要关闭? ​

【标准答案】自我保护机制是Eureka Server在短时间内丢失大量客户端心跳时,认为是网络故障而非服务宕机,于是停止剔除失效实例,保护注册表。

具体原理:当每分钟心跳数低于阈值(默认每分钟应收到心跳数 * 0.85)时触发,Eureka不再从注册表移除任何实例,即使客户端长时间不续约也保留。

生产环境建议:不要关闭。原因有三:

  1. 网络波动是常态,自我保护可防止因网络抖动导致服务被误剔除
  2. AP模型的设计初衷就是保证可用性,宁可保留过期实例也不能丢失正常实例
  3. 服务消费者有重试机制和熔断保护,即使调用到失效实例也能优雅降级

如果确实需要剔除,可调低阈值(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生态深度整合,支持服务发现自动发现

核心差异:

  1. 编程模型:Ribbon是阻塞式,LoadBalancer支持响应式
  2. 维护状态:Ribbon停止维护,LoadBalancer是官方推荐
  3. 扩展性:LoadBalancer通过ServiceInstanceListSupplier扩展更灵活

迁移建议:新项目直接用LoadBalancer,老项目可继续用Ribbon但逐步迁移。

【面试题】4. OpenFeign的超时重试怎么配置?有哪些坑? ​

【标准答案】OpenFeign有两个超时参数:

yaml
feign:
  client:
    config:
      default:
        connectTimeout: 5000  # 连接超时
        readTimeout: 10000    # 读取超时

重试机制:默认使用Retryer.NEVER_RETRY,即不重试。需自定义Retryer:

java
@Bean
public Retryer retryer() {
    return new Retryer.Default(100, 1000, 3); // 初始间隔100ms,最大1s,最多3次
}

常见坑点:

  1. 超时时间设置过短:没考虑网络波动,导致频繁超时重试
  2. 重试与业务幂等冲突:非幂等接口重试可能产生重复数据
  3. 全局配置覆盖局部:default配置会被特定服务配置覆盖,需注意优先级
  4. 超时叠加: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配置中心

java
@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:

java
@Autowired
private RouteDefinitionLocator routeDefinitionLocator;

public void refreshRoutes() {
    // 清除缓存,重新加载路由
    routeDefinitionLocator.getRouteDefinitions().subscribe();
}

【面试题】7. 分布式事务Seata的AT模式原理是什么?有什么缺陷? ​

【标准答案】AT模式是Seata默认的分布式事务模式,全称"Auto Transaction",核心是自动补偿。

工作原理:

  1. 一阶段:执行业务SQL,同时记录undo_log(前镜像+后镜像)
  2. 二阶段提交:删除undo_log
  3. 二阶段回滚:根据undo_log反向生成SQL,恢复数据

关键组件:

  • TC(Transaction Coordinator):事务协调者,维护全局事务状态
  • TM(Transaction Manager):事务管理者,发起全局事务
  • RM(Resource Manager):资源管理者,管理分支事务

缺陷与限制:

  1. 脏写风险:全局事务未提交时,被本地事务修改会导致数据不一致
    • 解决:使用全局锁,但影响性能
  2. 性能开销:需要写undo_log,增加IO和存储
  3. SQL限制:不支持复杂SQL(如多表关联、存储过程)
  4. 隔离级别:默认读未提交,可能出现脏读
  5. 大事务问题:长事务占用连接资源,需拆分

适用场景:对一致性要求不是极高、事务较短的微服务场景。

【面试题】8. Redis分布式锁用SET NX EX有什么问题?Redisson怎么解决? ​

【标准答案】SET NX EX是Redis实现分布式锁的简单方式:

bash
SET key value NX EX 30

存在问题:

  1. 锁超时释放:业务执行时间超过锁过期时间,锁被自动释放,其他线程获得锁
  2. 误删锁:线程A的锁过期被删,线程B获得锁,线程A执行完删了B的锁
  3. 不可重入:同一线程多次获取同一把锁会失败
  4. 单点故障:Redis主节点宕机,锁数据丢失

Redisson解决方案:

  1. 看门狗机制:后台线程定期续期,默认每10秒续期30秒,防止业务未完成锁过期
  2. Lua脚本保证原子性:加锁和删锁用Lua脚本,避免误删
  3. 可重入锁:用Hash结构存储,value记录重入次数
  4. RedLock算法:多Redis实例,半数以上加锁成功才算成功

使用示例:

java
RLock lock = redisson.getLock("myLock");
lock.lock(); // 自动续期
try {
    // 业务逻辑
} finally {
    lock.unlock();
}

【面试题】9. Spring Cloud Stream如何保证消息可靠性投递? ​

【标准答案】消息可靠性涉及三个阶段:发送端、中间件、消费端。

发送端保障:

yaml
spring:
  cloud:
    stream:
      bindings:
        output:
          producer:
            required-groups: group1  # 确保消息持久化

配置说明:

  • required-groups:指定消费者组,确保消息持久化到队列
  • 开启事务消息(RocketMQ/Kafka支持)

中间件保障:

  • 刷盘机制:同步刷盘保证不丢消息,异步刷盘提高性能
  • 主从同步:同步复制保证高可用,异步复制提高吞吐

消费端保障:

java
@StreamListener(Sink.INPUT)
public void handle(Message<String> message) {
    try {
        process(message);
        // 手动ACK
    } catch (Exception e) {
        // 不ACK,触发重试
        throw e;
    }
}

配置重试策略:

yaml
spring:
  cloud:
    stream:
      bindings:
        input:
          consumer:
            max-attempts: 3  # 重试次数
            back-off-initial-interval: 1000
            back-off-multiplier: 2.0

死信队列:超过重试次数后进入DLQ,需单独处理。

最佳实践:发送端确认+消费端手动ACK+死信队列兜底。

【面试题】10. 微服务架构下如何实现灰度发布? ​

【标准答案】灰度发布核心是按比例或按条件分流,逐步验证新版本。

实现方案:

1. 基于Gateway路由:

yaml
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, v2

2. 基于Nacos元数据:

  • 服务实例注册时带上metadata(version=v2)
  • Ribbon根据元数据过滤实例
  • 结合ThreadLocal传递版本标识

3. 专业方案:Istio Service Mesh:

  • VirtualService配置流量比例
  • DestinationRule定义服务子集
  • 支持基于Header、Cookie、权重的精细分流

灰度策略:

  • 金丝雀发布:先让5%流量到新版本,逐步扩大
  • 蓝绿部署:新版本独立部署,一键切换
  • A/B测试:按用户特征分流,对比效果

关键点:

  1. 服务间调用链路需传递版本标识(Header透传)
  2. 数据库兼容:新版本向后兼容旧版本数据
  3. 回滚机制:新版本异常可快速切回旧版本
  4. 监控告警:灰度期间密切监控错误率和性能指标