Skip to content

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

【面试题】1. Eureka自我保护机制什么情况下会触发?生产环境要不要关闭? ​

【标准答案】Eureka自我保护机制在短时间内大量服务实例心跳丢失(超过85%阈值)时触发。触发后,Eureka不再剔除任何服务,保护注册表不被清空。

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

  1. 防止网络分区误杀:网络抖动时,若关闭自我保护,Eureka会大量剔除正常服务,导致服务雪崩。
  2. 保护优先级高于准确:宁可保留已下线的"僵尸服务",也不能把正常服务误剔除(AP模型的典型体现)。
  3. 快速恢复:网络恢复后,心跳恢复正常,自我保护自动解除。

如果担心僵尸服务问题,可以配合服务健康检查和客户端重试机制,而不是关闭自我保护。


【面试题】2. Ribbon如何自定义负载均衡策略?举例说明。 ​

【标准答案】自定义负载均衡策略有两种方式:

方式一:配置文件指定

yaml
userservice:
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

方式二:代码自定义IRule实现类

java
public class MyWeightedRule extends AbstractLoadBalancerRule {
    @Override
    public Server choose(ILoadBalancer lb, Object key) {
        // 自定义逻辑:如按响应时间加权、按区域优先等
        return server;
    }
}

实际场景举例:

  • 按版本灰度发布:根据服务metadata中的version字段,90%流量走v1,10%走v2。
  • 按响应时间动态权重:响应快的实例权重更高,慢的降权,实现自适应负载均衡。
  • 同区域优先:优先调用同可用区的实例,降低跨区域延迟。

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

【标准答案】OpenFeign超时配置分两层:

yaml
feign:
  client:
    config:
      default:
        connectTimeout: 5000  # 连接超时
        readTimeout: 10000    # 读取超时
  retryer:
    period: 100      # 重试间隔
    maxPeriod: 1000  # 最大重试间隔
    maxAttempts: 3   # 最大重试次数(包含首次)

常见坑点:

  1. 默认重试策略是Retryer.NEVER_RETRY:如果不显式配置,失败后不会重试。
  2. Ribbon也有超时配置:如果Ribbon的timeout < Feign的readTimeout,请求会被Ribbon提前掐断。
  3. 重试可能造成幂等性问题:POST/PUT接口默认重试可能导致重复提交,建议接口设计为幂等,或对非幂等接口关闭重试。
  4. 超时过长拖垮系统:如果下游服务响应慢,Feign一直等待,会占用连接池,建议设置合理超时并配合熔断。

【面试题】4. Sentinel的限流算法有哪些?各自优缺点? ​

【标准答案】Sentinel支持三种主流限流算法:

算法原理优点缺点
令牌桶固定速率生成令牌,请求获取令牌后执行允许一定程度的突发流量(桶内有令牌即可)需要预分配令牌空间
漏桶请求先进队列,固定速率流出输出平滑,保护下游无法应对突发流量,队列可能溢出
滑动窗口将时间窗口分成多个格子,统计每个格子的请求数统计精准,适合QPS限流内存占用略高

实际选型建议:

  • API网关限流:用滑动窗口,精准控制每秒请求数。
  • 数据库保护:用令牌桶,允许短时突发,但长期平滑。
  • 外部调用保护:用漏桶,固定速率调用,避免压垮下游。

【面试题】5. Spring Cloud Gateway如何处理跨域? ​

【标准答案】Gateway处理跨域有两种方式:

方式一:全局配置

yaml
spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allowedOrigins: "https://example.com"
            allowedMethods: "*"
            allowedHeaders: "*"
            allowCredentials: true
            maxAge: 3600

方式二:路由级别配置

yaml
spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/api/user/**
          metadata:
            cors:
              allowedOrigins: "'https://example.com'"

注意事项:

  1. 不要配置allowedOrigins: "*"同时allowCredentials: true,浏览器会报错,必须明确指定域名。
  2. 生产环境限制allowedOrigins,只允许受信任的前端域名。
  3. 预检请求(OPTIONS)单独处理:Gateway会自动响应OPTIONS请求,无需转发到后端。

【面试题】6. Nacos配置热更新的原理是什么?如何实现配置实时推送? ​

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

核心机制:长轮询 + 监听器

  1. 客户端启动时注册监听器:
java
configService.addListener("app.yml", "DEFAULT_GROUP", new Listener() {
    @Override
    public void receiveConfigInfo(String configInfo) {
        // 配置变更回调,更新本地配置
    }
});
  1. 长轮询机制:

    • 客户端向Nacos发起长轮询请求(默认30秒超时)
    • 服务端收到请求后,检查配置版本号
    • 若版本号一致(无变更),挂起请求
    • 若版本号不一致(有变更),立即返回新配置
    • 超时未变更,返回304,客户端重新发起长轮询
  2. 实时性保证:

    • 配置变更后,Nacos主动推送变更到所有监听的客户端
    • 推送失败时,客户端下次长轮询时拉取最新配置

与Spring Cloud集成:

  • @RefreshScope注解标记的Bean会在配置变更时自动刷新
  • 配置变更后,Spring Cloud Context发布RefreshEvent,触发Bean重建

【面试题】7. Sleuth如何实现TraceId在微服务间的传递? ​

【标准答案】Sleuth通过HTTP Header传递实现TraceId跨服务传递:

核心流程:

  1. 请求到达网关/首个服务:

    • Sleuth生成TraceId(如:5e6a3d1f2b7c4a8b)和SpanId
    • 将TraceId写入HTTP Header:X-B3-TraceId
  2. 服务A调用服务B:

    • RestTemplate/Feign拦截器自动从当前上下文读取TraceId
    • 将Header透传:X-B3-TraceId、X-B3-SpanId、X-B3-ParentSpanId
  3. 服务B接收请求:

    • Sleuth Filter从Header提取TraceId
    • 创建新的Span,继承相同的TraceId
    • 继续向下游服务传递

关键Header:

  • X-B3-TraceId:全局唯一,贯穿整个调用链
  • X-B3-SpanId:每个服务的唯一Span标识
  • X-B3-ParentSpanId:父Span的ID

异步调用场景:

  • 使用@NewSpan手动创建Span
  • 使用Sleuth提供的Runnable/Callable包装器,自动传递上下文

【面试题】8. Seata AT模式如何保证分布式事务一致性?有什么缺点? ​

【标准答案】Seata AT模式原理:

核心机制:两阶段提交 + 自动补偿

第一阶段(执行业务SQL):

  1. 解析业务SQL,记录before image(修改前的数据快照)
  2. 执行业务SQL
  3. 记录after image(修改后的数据快照)
  4. 生成行锁(防止脏写)
  5. 将before/after image和行锁写入undo_log表

第二阶段(提交/回滚):

  • 提交:异步删除undo_log,释放行锁
  • 回滚:根据undo_log反向生成SQL,恢复数据

优点:

  • 业务无侵入,自动补偿
  • 性能较好,本地事务先提交,不阻塞其他事务

缺点:

  1. 脏读风险:一阶段提交后,其他事务可能读到未最终确认的数据(隔离级别问题)
  2. 全局锁竞争:高并发下,多个全局事务竞争同一行数据的锁,性能下降
  3. undo_log表膨胀:事务量大时,undo_log表增长快,需定期清理
  4. 不支持复杂SQL:如存储过程、多表关联更新等

适用场景: 大部分业务场景,尤其是对隔离性要求不高的场景。


【面试题】9. Redisson的RedLock(红锁)是什么?解决了什么问题? ​

【标准答案】RedLock是Redis官方提出的分布式锁高可用方案:

解决的问题:主从架构下的锁丢失

传统Redis主从架构:

  • 客户端A在Master上加锁成功
  • Master宕机,锁未同步到Slave
  • Slave升级为Master
  • 客户端B也加锁成功 → 锁失效,两个客户端同时持有锁

RedLock原理:

  1. 获取当前时间戳
  2. 依次向N个(通常5个)独立的Redis节点申请加锁
  3. 计算加锁成功耗时,若超过锁过期时间,则加锁失败
  4. 若超过半数(N/2+1)节点加锁成功,且总耗时小于锁过期时间,则加锁成功
  5. 释放锁时,向所有节点发送解锁请求

代码示例:

java
RedissonClient client1 = Redisson.create(config1);
RedissonClient client2 = Redisson.create(config2);
RedissonClient client3 = Redisson.create(config3);

RLock lock1 = client1.getLock("myLock");
RLock lock2 = client2.getLock("myLock");
RLock lock3 = client3.getLock("myLock");

RedissonRedLock redLock = new RedissonRedLock(lock1, lock2, lock3);
redLock.lock();
try {
    // 业务逻辑
} finally {
    redLock.unlock();
}

争议点: RedLock在时钟漂移、网络分区等极端场景下仍有问题,实际生产中更多采用单节点Redis + 主从切换+锁续期的方案。


【面试题】10. JWT Token过期后如何实现无感刷新? ​

【标准答案】JWT无感刷新方案:

方案一:双Token机制(Access Token + Refresh Token)

  1. 登录时返回两个Token:

    • Access Token:有效期短(如15分钟)
    • Refresh Token:有效期长(如7天)
  2. 请求拦截器处理:

javascript
// 请求拦截器
axios.interceptors.request.use(config => {
    const accessToken = localStorage.getItem('accessToken');
    config.headers.Authorization = `Bearer ${accessToken}`;
    return config;
});

// 响应拦截器
axios.interceptors.response.use(
    response => response,
    async error => {
        if (error.response.status === 401) {
            const refreshToken = localStorage.getItem('refreshToken');
            const res = await axios.post('/auth/refresh', { refreshToken });
            localStorage.setItem('accessToken', res.data.accessToken);
            // 重新发起原请求
            return axios(error.config);
        }
        return Promise.reject(error);
    }
);
  1. 服务端刷新逻辑:
    • 校验Refresh Token有效性
    • 若有效,生成新的Access Token返回
    • 若无效,返回401,前端跳转登录页

方案二:Token快过期时主动刷新

  • 前端检查Token过期时间,剩余时间小于阈值时主动刷新
  • 避免请求时突然401,体验更好

安全注意事项:

  1. Refresh Token存储在HttpOnly Cookie中,防止XSS攻击
  2. Refresh Token使用后立即失效,生成新的(防止重放攻击)
  3. 敏感操作(如修改密码)要求重新登录,不能用Refresh Token