Skip to content

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

生成时间:2026-06-19 23:10

【面试题】1. Nacos既支持AP又支持CP模式,什么场景切AP?什么场景切CP?切换有什么代价? ​

【标准答案】先说结论:注册中心用AP,配置中心用CP,这是Nacos设计的默认行为。 AP模式(最终一致性):适合服务发现,注册中心挂了不影响已缓存的调用链路,优先保证可用性。CP模式(强一致性):适合配置管理,必须保证所有节点读到同一份配置,不能出现"机器A读旧配置、机器B读新配置"的情况。Nacos通过distro协议支持临时实例(AP)和raft协议支持持久化实例(CP)。切换代价主要有两点:一是CP模式下写性能明显下降(Raft需要过半节点确认),二是CP模式在网络分区时可能牺牲可用性——如果Leader与过半Follower断连,整个集群拒绝写入。实际生产建议:服务注册用临时实例(AP),需要持久化元数据时才开CP。


【面试题】2. Spring Cloud LoadBalancer替代Ribbon后,自定义负载均衡策略怎么做?和Ribbon写法有什么本质区别? ​

【标准答案】Ribbon是Netflix的,已经进入维护模式,Spring官方推LoadBalancer接力。自定义策略步骤:实现ReactorServiceInstanceLoadBalancer接口,核心方法是choose(Request request),返回Mono<Response<ServiceInstance>>。和Ribbon的本质区别:Ribbon是命令式/同步模型,LoadBalancer是基于Reactor的响应式模型。 Ribbon的IRule返回一个Server对象,LoadBalancer返回Mono——这意味着整个选择流程是非阻塞的,可以和WebFlux无缝集成。具体实现时,你可以从ServiceInstanceListSupplier拿到实例列表,然后按自己的算法(比如按实例metadata中的zone、version做亲和性路由)筛选,最后包装成Response返回。配置方式:写一个@Bean注入ReactorLoadBalancer<服务名>即可,Spring Cloud会自动发现并替换默认的轮询策略。


【面试题】3. OpenFeign调用超时后自动重试,怎么配才能避免"一个请求执行两次"? ​

【标准答案】这个问题是经典坑。OpenFeign底层走Ribbon或LoadBalancer,重试有两个层级:Feign自己的重试和Ribbon/LoadBalancer的重试。如果你两处都配了重试,可能出现"Feign超时重试1次 + Ribbon超时重试1次 = 同一个请求打了3次"。避免方案:第一,明确只有幂等接口(GET、PUT、DELETE)才允许重试,POST/非幂等操作必须关闭重试——Ribbon默认只对GET重试,但可以通过ribbon.OkToRetryOnAllOperations=false显式关闭。第二,精简重试链路:建议只用Ribbon重试,Feign层面关掉(feign.client.config.default.retryer=不重试),统一在Ribbon配置MaxAutoRetries和MaxAutoRetriesNextServer。第三,如果用了Spring Cloud LoadBalancer,需要在spring.cloud.loadbalancer.retry.retry-on-all-operations=false显式关掉非GET重试。核心原则:非幂等操作宁可失败让用户重试,也别让系统帮你偷偷重试。


【面试题】4. Sentinel和Hystrix对比,为什么现在新项目普遍选Sentinel? ​

【标准答案】Hystrix是熔断领域的先驱,但2018年宣布停止维护。Sentinel的优势非常具体:第一,更丰富的流控规则——Hystrix只有熔断和线程池隔离,Sentinel还支持QPS限流、热点参数限流、系统自适应保护、黑白名单控制,粒度从接口级别到参数级别都能控制。第二,实时监控和Dashboard——Sentinel的控制台可以实时看QPS、RT、线程数,还能在控制台动态改规则并推送到应用,Hystrix的dashboard只能看不能改。第三,资源消耗更小——Hystrix每个命令默认用一个线程池,100个接口就是100个线程池,资源开销大;Sentinel默认用滑动窗口计数器统计,无需额外线程。第四,规则持久化——Sentinel的规则可以推到Nacos/Apollo持久化,应用重启不丢规则,Hystrix规则是内存级的。总结:Sentinel是Hystrix的全面升级版,功能多、性能好、运维友好。


【面试题】5. Spring Cloud Gateway的核心工作流程是什么?一个请求从进到出经过了哪些关键步骤? ​

【标准答案】Gateway底层基于Netty + WebFlux,全链路非阻塞。请求流程分五步:第一步,客户端请求到达Netty服务器,由ReactorHttpHandlerAdapter适配到WebFlux的DispatcherHandler。第二步,RoutePredicateHandlerMapping根据路由断言(Predicate)匹配路由——比如Path=/api/user/**匹配到user-service路由。第三步,匹配成功后,请求进入过滤器链(Filter Chain)——这是Gateway的核心。过滤器分"pre"和"post"两种:pre过滤器在转发前执行(如鉴权、限流、参数校验),post过滤器在响应返回时执行(如添加响应头、日志记录)。第四步,NettyRoutingFilter把请求转发到下游服务,这里用的是Netty的HttpClient,非阻塞IO。第五步,下游响应回来后,post过滤器逆序执行,最终返回给客户端。面试重点:整个链路是响应式的,没有Servlet容器的线程模型,一个请求不独占一个线程,所以Gateway的吞吐量远高于Zuul 1.x。


【面试题】6. Nacos Config配置热更新有几种实现方式?各自的适用场景是什么? ​

【标准答案】三种方式,覆盖不同场景。第一种:@RefreshScope + @Value——这是最常用的方式。被@RefreshScope标注的Bean,在Nacos配置变更后会销毁并重建,@Value注入的值随之更新。注意@RefreshScope是懒加载的,只在第一次调用时才创建Bean,高并发场景可能需要预热。第二种:@RefreshScope + @ConfigurationProperties——推荐用于批量配置场景,把一组配置映射到一个配置类,变更后自动刷新整个对象,比一个个@Value优雅。第三种:手动监听RefreshEvent——最灵活的方式。实现ApplicationListener<RefreshScopeRefreshedEvent>或在任意Bean中监听这个事件,拿到变更后做自定义处理(比如重连数据库、重建连接池)。如果配置变更涉及数据库连接、Redis地址等需要重建资源的场景,前两种方式不够用,必须用第三种手动处理。另外Nacos 2.x还支持通过@NacosConfigListener注解直接监听特定配置项,写法更简洁。


【面试题】7. Sleuth+Zipkin链路追踪中,TraceId和SpanId是怎么在微服务之间传递的?如果中间经过MQ,链路会不会断? ​

【标准答案】TraceId和SpanId通过HTTP请求头传递,Sleuth会自动注入两个关键头:X-B3-TraceId(全链路唯一标识)和X-B3-SpanId(当前Span标识)。调用链路是:服务A生成TraceId和SpanId → 通过Feign/RestTemplate的拦截器自动把这两个头塞到请求里 → 服务B的Filter收到请求后,从Header中提取TraceId,生成新的SpanId(它的父SpanId就是服务A的SpanId)。整条链路的父子关系靠ParentSpanId串联。经过MQ确实会断——因为MQ是异步解耦的,生产者和消费者的Span不是同一个线程上下文。解决方案是:生产者发送消息时,手动把TraceId塞到消息Header里(RocketMQ用UserProperty,Kafka用Header);消费者收到消息后,用Tracer.nextSpan()手动创建Span,把提取的TraceId作为父TraceId,这样链路就续上了。Sleuth 3.x之后配合Micrometer Tracing,API变成了Observation,但传递原理不变——都是靠Header传trace context。


【面试题】8. Seata AT模式和TCC模式有什么区别?怎么选? ​

【标准答案】AT模式是自动挡,TCC模式是手动挡。AT模式原理:一阶段自动拦截SQL,解析SQL的前镜像和后镜像(UNDO_LOG),直接提交事务;二阶段如果全局回滚,根据UNDO_LOG反向生成补偿SQL。优点是业务代码零侵入,加个@GlobalTransactional就行。缺点是只支持关系型数据库(依赖ACID和UNDO_LOG表),性能有额外开销(每次写SQL都要生成UNDO_LOG)。TCC模式:业务代码自己实现Try-Confirm-Cancel三个接口。Try阶段预留资源(冻结库存、预扣余额),Confirm提交(真正扣库存、扣余额),Cancel回滚(解冻库存、退回余额)。优点是性能好(一阶段直接提交,无UNDO_LOG开销),支持任何数据源(Redis、MQ等)。缺点是代码侵入大,每个业务都要写三套逻辑,而且要考虑空回滚、悬挂、幂等。怎么选?金融交易、库存扣减等需要高性能且愿意写补偿代码的场景选TCC;通用CRUD业务、快速开发的场景选AT。如果团队业务逻辑复杂、对数据一致性要求极高,TCC是首选,否则AT够用。


【面试题】9. Redisson的看门狗(Watchdog)机制是怎么保证分布式锁不超时释放的? ​

【标准答案】用Redis做分布式锁最大的坑是:锁设置了过期时间,但业务还没执行完锁就过期了,别的线程拿到锁,就出现并发安全问题。Redisson的看门狗就是解决这个问题的。工作原理:当线程通过RLock.lock()加锁时,如果没有显式指定leaseTime,Redisson默认给锁设30秒过期时间,然后启动一个后台定时任务,每10秒(30秒的1/3)检查一下"这个锁是不是还被当前线程持有",如果是就把过期时间续期回30秒。这个续期是持续性的,直到业务执行完主动调用unlock(),看门狗才会停止。关键细节:看门狗的续期操作是用Lua脚本保证原子性的——先检查锁的value(UUID:ThreadId)是否匹配,匹配才执行PEXPIRE续期。如果Redisson客户端挂了,看门狗也停了,锁最多30秒后自动释放,不会死锁。注意:只有不指定leaseTime时看门狗才生效;如果手动设了leaseTime(比如lock(10, TimeUnit.SECONDS)),看门狗不会启动,锁到点就释放,不会续期。


【面试题】10. RocketMQ顺序消息怎么保证?如果消费过程中某个消息处理失败,会不会阻塞整个队列? ​

【标准答案】RocketMQ的顺序消息分两步保证。生产端:通过MessageQueueSelector把同一业务ID(比如订单ID)的消息路由到同一个MessageQueue,确保发往同一个Queue的消息是有序的。消费端:消费者注册MessageListenerOrderly,RocketMQ保证同一个Queue的消息被同一个线程串行消费,前一条ACK后才会拉取下一条。默认会阻塞——某个消息消费失败后,RocketMQ会不断重试(SUSPEND_CURRENT_QUEUE_A_MOMENT),重试间隔逐渐变长(10s → 30s → 1min → 2min...),这期间整个Queue被卡住,后面的消息全部等待。生产环境怎么处理?三个策略:一是消费逻辑加超时控制和合理的重试上限(setMaxReconsumeTimes),超过上限后发到死信队列,人工处理;二是业务上做幂等和快速失败,尽量避免单条消息卡死;三是评估业务是否真的需要全局顺序——大多数场景分区有序就够用了(同一个订单的消息有序即可,不同订单不需要有序),分区有序天然隔离了故障影响面。如果真的需要全局顺序又怕阻塞,可以考虑给消费者加降级开关,异常时跳到死信Topic让运维介入。


📁 文件路径:/Users/pika/fsf/project/fsf-lunar-base/note/10-面试题/SpringCloud微服务面试题_20260619_2310.md 📊 题目数量:10 道 | 覆盖考点:Nacos AP/CP、LoadBalancer、OpenFeign、Sentinel、Gateway、Nacos Config、Sleuth/Zipkin、Seata、Redisson、RocketMQ