SpringCloud微服务架构面试题库
生成时间:2026-05-23 16:19 | 题目数:10
【面试题】1. Nacos既支持AP又支持CP,它是怎么切换的?什么场景下用AP,什么场景下用CP?
【标准答案】Nacos通过临时实例和持久实例来区分AP与CP模式。临时实例(ephemeral=true,默认)走AP模式,使用Distro协议,客户端心跳保活,注册中心不保证强一致;持久实例(ephemeral=false)走CP模式,使用Raft协议,服务端主动探测,保证数据一致性。切换方式就是在注册时设置ephemeral参数。实际场景:业务服务实例频繁上下线用AP,因为可用性优先;数据库、缓存等基础设施服务用CP,因为数据不能丢、不能乱。面试官追问点:Nacos 2.x之后引入gRPC长连接替代心跳,临时实例的感知速度从30秒缩短到3秒以内,但本质上还是AP。
【面试题】2. Sentinel的熔断降级和Hystrix的熔断降级核心区别是什么?生产环境选哪个?
【标准答案】核心区别三点:第一,熔断策略更丰富——Hystrix只有异常比例和超时两种,Sentinel额外支持慢调用比例和异常数策略,对RT敏感场景更友好。第二,限流能力——Hystrix本身不做限流,靠线程池隔离间接控制并发;Sentinel原生支持QPS限流、线程数限流、热点参数限流,内置令牌桶/漏桶/滑动窗口算法。第三,扩展性——Hystrix已停止维护,Sentinel是Alibaba持续迭代的,控制台可视化更完善,支持集群限流。生产建议:新项目直接Sentinel,老项目迁移也值得;如果团队深度用Spring Cloud Netflix栈且暂时没精力迁移,Hystrix也能扛,但要做好长期规划。
【面试题】3. Spring Cloud Gateway的过滤器链执行顺序是怎样的?如何实现全局鉴权并让某些路由跳过鉴权?
【标准答案】Gateway过滤器分三类:Global Filter → Gateway Filter → Routing Filter,执行顺序按Ordered接口的order值从小到大。请求进来依次执行pre阶段,响应回来逆序执行post阶段。全局鉴权实现:写一个GlobalFilter,order设为最高优先级(如-1),在filter方法中校验Token。跳过鉴权的方式有两种:第一种,在GlobalFilter中通过exchange.getRequest().getURI().getPath()判断路径,白名单路径直接chain.filter放行;第二种,利用GatewayFilter在特定路由上配置,白名单路由不挂载鉴权Filter。推荐第一种,集中管理白名单更清晰。面试加分点:可以说Token校验失败时用exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED)直接短路返回,不继续过滤链。
【面试题】4. Seata的AT模式两阶段提交具体怎么执行的?undo_log表是干什么用的?
【标准答案】AT模式是一阶段直接提交本地事务+记录回滚日志,二阶段异步清理或回滚。一阶段:拦截业务SQL,查询修改前数据生成beforeImage,执行业务SQL,查询修改后数据生成afterImage,把beforeImage+afterImage+业务SQL组成undo_log记录插入undo_log表,然后提交本地事务。二阶段如果是提交:异步删除undo_log记录,很快。二阶段如果是回滚:根据undo_log的beforeImage反向生成补偿SQL执行回滚,然后删除undo_log。undo_log表就是AT模式的核心,存的是前后镜像数据,保证了即使一阶段已提交,二阶段也能准确回滚。注意点:每个参与事务的微服务数据库都要建undo_log表,全局事务结束后要及时清理,避免膨胀。另外AT模式有全局锁机制,一阶段提交后本地锁释放但全局锁持有,防止脏写。
【面试题】5. Redis分布式锁用SET NX EX有什么问题?Redisson是怎么解决的?
【标准答案】SET NX EX三个问题:第一,锁续期——业务没执行完但锁已过期,其他线程拿到锁导致并发问题。第二,锁释放误删——线程A的锁过期后,线程B拿到锁,线程A执行完执行DEL把B的锁删了。第三,非原子性——SET NX和EXPIRE分开执行,程序崩溃会导致死锁。Redisson解决方案:第一,看门狗机制(Watchdog)——默认锁30秒,后台线程每10秒(1/3租期)自动续期,业务没完成就一直续,业务完成就不续,完美解决锁续期问题。第二,Lua脚本保证原子性——加锁和解锁都用Lua脚本执行,解锁时先判断value是不是自己设的,是才删除,解决误删问题。value通常用UUID+线程ID。第三,可重入锁——用Hash结构记录锁持有者和重入次数,同一线程可多次获取。面试加分:提一下RedLock多节点锁的争议,Martin Kleppmann和Antirez的论战,生产中单节点Redisson够用,极端场景考虑RedLock或Zookeeper锁。
【面试题】6. OpenFeign的超时和重试配置如何设计才能避免请求雪崩?
【标准答案】关键原则:超时要短、重试要少、重试要幂等。具体配置:ConnectTimeout设2-3秒(建立连接超时),ReadTimeout设3-5秒(读取数据超时),根据P99 RT调整。重试方面:默认Feign不重试,如果要开启,MaxAutoRetries=0(当前实例不重试),MaxAutoRetriesNextServer=1(换一个实例重试一次),总重试次数最多1次。绝不能对非幂等接口(POST/PUT)重试,只对GET重试,或者自定义Retryer判断HTTP方法。防雪崩的关键:超时+重试的合计时间必须远小于上游服务的超时时间,形成超时时间漏斗——网关5秒→服务A 3秒→服务B 2秒,每一层都比上一层短,确保快速失败不拖垮整条链路。另外配合Sentinel熔断,连续失败超过阈值直接熔断,不再发起请求。
【面试题】7. 链路追踪中TraceId是如何跨服务传递的?异步线程场景下TraceId会丢失怎么办?
【标准答案】同步调用链路:Sleuth在HTTP请求头中自动注入traceId(X-B3-TraceId)和spanId(X-B3-SpanId),下游服务从请求头中提取,继续传递,整条链路traceId不变、spanId逐级生成。跨进程通过HTTP Header传递,跨线程通过ThreadLocal传递。异步线程丢失问题:主线程的TraceId存在ThreadLocal里,子线程无法继承。解决方案三种:第一,Sleuth自带LazyTraceExecutor,包装线程池自动传递;第二,手动用SpanDecorator或Tracer将traceId传入子线程;第三,用@Async时配置自定义TaskExecutor包装Sleuth的TraceableExecutor。最推荐第一种,代码侵入最小。面试加分点:提一下消息队列场景(Kafka/RocketMQ),TraceId通过消息Header传递,Sleuth对Spring Cloud Stream有自动集成,但RocketMQ需要手动埋点。
【面试题】8. Nacos Config配置热更新的原理是什么?@RefreshScope和@Value注解有什么区别?
【标准答案】热更新原理:Nacos Client长轮询服务端,默认每30秒检查配置变更(2.x用gRPC长连接实时推送),检测到变化后发布RefreshEvent事件,Spring Cloud Context接收到事件后销毁并重建@RefreshScope标注的Bean,新Bean从最新配置中读取值。@RefreshScope vs @Value:@Value注入的值是Bean创建时绑定的,配置变更后值不变,除非所在Bean加了@RefreshScope;@RefreshScope标注的Bean在配置变更时会被销毁重建,下次访问时从最新配置取值。最佳实践:配置类用@ConfigurationProperties + @RefreshScope,比@Value更规范,支持属性批量绑定和校验。注意坑:@RefreshScope的Bean是懒加载代理,首次访问才创建,如果Bean初始化很重会有首次请求慢的问题;另外频繁变更配置会导致Bean反复重建,要注意控制变更频率。
【面试题】9. Kubernetes环境下Spring Cloud服务注册发现该用Nacos还是K8s Service?两者冲突怎么办?
【标准答案】核心思路:看你的服务调用方式。如果全部走K8s集群内部调用,用K8s Service + Spring Cloud Kubernetes就够了,不需要额外注册中心,利用K8s原生Service发现和负载均衡,运维成本低。如果有部分服务在K8s外(比如遗留系统、混合云),用Nacos统一注册发现更合适。冲突场景:Spring Cloud应用同时引入Nacos和K8s Service发现,导致一个服务注册两份、负载均衡出问题。解决方案:二选一,要么用spring-cloud-kubernetes依赖禁用Nacos,要么用Nacos时排除spring-cloud-kubernetes自动配置。迁移策略:灰度切换,先双注册(两边都注册),消费端逐步从K8s Service切到Nacos,切完后下掉K8s Service发现。面试加分:Service Mesh场景下,Istio接管流量管理,应用层只做业务逻辑,服务发现和负载均衡下沉到Sidecar,这时候Spring Cloud的注册发现可以完全交给Istio。
【面试题】10. 设计一个微服务灰度发布的方案,要求用户维度路由、支持快速回滚,怎么实现?
【标准答案】分三层实现:第一层,网关路由——Spring Cloud Gateway自定义GlobalFilter,根据请求头(如userId、tag)匹配灰度规则,通过自定义负载均衡器将请求路由到灰度版本实例。Nacos支持实例metadata,灰度实例打上version=gray标签。第二层,服务间调用透传——在Gateway设置灰度标记到HTTP Header(如X-Gray-Tag),下游服务通过RequestInterceptor(Feign)或HandoffInterceptor(RestTemplate)透传灰度标记,自定义Ribbon/LoadBalancer规则读取标记优先路由灰度实例。第三层,快速回滚——灰度实例和正式实例同时部署,回滚只需在Nacos/Nacos Config修改灰度规则(如将灰度比例从10%改为0%),或者直接摘除灰度实例路由权重,秒级生效不需要重新部署。用户维度路由:根据userId哈希或白名单决定是否走灰度,保证同一用户始终路由到同一版本,避免版本切换导致状态不一致。进阶:配合Sentinel的流量控制做灰度比例精确控制,配合SkyWalking对比灰度与正式版本的RT和错误率。