本轮 SpringCloud微服务架构面试题库(带答案)
生成时间:2026-05-21 23:59
【面试题】1. Nacos配置中心如何实现配置的热更新?项目中如何让配置变更实时生效?
【标准答案】 Nacos配置热更新有两种方式:一是通过@RefreshScope注解,二是通过@NacosValue注解结合配置监听。
最常用的是@RefreshScope,在需要动态刷新的Bean上加这个注解,Nacos配置变更时会触发Bean的重新创建,从而加载最新配置。底层原理是Nacos客户端通过长轮询机制监听配置变化,服务端有变更时立即推送,客户端收到后发布RefreshEvent事件,Spring Cloud的ContextRefresher处理这个事件,更新Environment中的配置源,然后销毁并重建标注了@RefreshScope的Bean。
实际项目中,配置类或Controller上加@RefreshScope,属性用@Value("${key}")注入即可。但要注意,@RefreshScope会导致Bean代理化,不要滥用,只在确实需要动态更新的Bean上加。另外,如果配置变更需要执行特定逻辑,可以监听RefreshScopeRefreshedEvent事件做额外处理。
【面试题】2. Sentinel的限流算法有哪些?令牌桶和漏桶算法的区别是什么?
【标准答案】 Sentinel支持多种限流算法:QPS限流、线程数限流,底层实现基于滑动窗口、令牌桶、漏桶三种算法。
滑动窗口算法:将时间窗口划分为多个小格子,每个格子记录请求数,滑动统计总请求数。Sentinel默认用滑动窗口实现QPS限流,精确度高,适合突发流量控制。
令牌桶算法:以固定速率往桶里放令牌,请求来了从桶里取令牌,有令牌就通过,没令牌就拒绝。特点是允许一定程度的突发流量——桶里有存量令牌时可以瞬间处理多个请求。Sentinel的RateLimiterController就是令牌桶实现。
漏桶算法:请求像水一样流入桶里,桶底以固定速率漏水(处理请求),桶满了就溢出(拒绝)。特点是强制平滑流量,输出速率恒定,无法应对突发。Sentinel的ThrottlingController类似漏桶思想。
核心区别:令牌桶允许突发(桶里有存量),漏桶强制匀速;令牌桶是"按需取令牌",漏桶是"排队等处理"。实际项目中,API网关限流常用令牌桶,因为前端流量本身有波动,允许小范围突发更合理;如果是调用外部API怕把对方打挂,用漏桶更稳。
【面试题】3. Seata的AT模式是如何实现分布式事务的?有什么优缺点?
【标准答案】 Seata AT模式是阿里开源的分布式事务解决方案,核心是"自动补偿"——框架自动生成回滚SQL,业务无侵入。
执行流程:一阶段,每个分支事务执行业务SQL时,Seata会解析SQL,记录数据修改前后的快照(before-image、after-image)到undo_log表,然后提交本地事务。二阶段如果全局事务提交,异步删除undo_log;如果回滚,根据before-image自动生成反向SQL执行补偿。
核心组件:
- TC(Transaction Coordinator):事务协调者,维护全局事务状态
- TM(Transaction Manager):事务管理器,发起全局事务
- RM(Resource Manager):资源管理器,管理分支事务
优点:业务代码零侵入,只需加@GlobalTransactional注解;性能好,一阶段直接提交,不阻塞;学习成本低,基本不用改代码。
缺点:依赖数据库本地事务,不支持跨库关联查询;全局锁可能导致死锁;高并发下undo_log表有性能瓶颈;隔离级别默认读未提交,脏读问题需要注意。另外,AT模式对SQL有要求,复杂SQL可能解析失败。
适用场景:单体拆分的微服务、对性能要求高、业务逻辑相对简单的场景。如果是跨库复杂业务或对隔离性要求高,建议用TCC模式。
【面试题】4. Redis分布式锁如何解决锁续期问题?Redisson的看门狗机制是什么?
【标准答案】 Redis分布式锁的续期问题是指:锁设置了过期时间,但业务执行时间超过了过期时间,导致锁自动释放,其他线程拿到锁后产生并发问题。
解决方案:Redisson的Watchdog(看门狗)机制。
原理:Redisson加锁时,如果不指定leaseTime,会启动一个后台线程(看门狗),默认每10秒检查一次锁是否还存在,如果存在就自动续期到30秒。只要客户端还活着且锁未显式释放,看门狗就会持续续期,直到业务执行完毕显式解锁。
代码层面:
RLock lock = redisson.getLock("myLock");
lock.lock(); // 启动看门狗,自动续期
try {
// 业务逻辑,执行时间不确定
} finally {
lock.unlock(); // 显式释放,看门狗停止
}关键参数:
lockWatchdogTimeout:默认30秒,锁的过期时间- 续期间隔:默认过期时间的1/3,即10秒
注意事项:如果指定了leaseTime(lock.lock(10, TimeUnit.SECONDS)),看门狗不会启动,业务必须在指定时间内完成;看门狗只在Redisson客户端存活时工作,客户端宕机则锁自动过期。
生产建议:Redisson内部已实现看门狗,直接用lock.lock()即可,无需手动实现续期逻辑;但要做好超时监控,如果业务真的执行太久,需要考虑是否需要优化业务逻辑或换方案。
【面试题】5. Spring Cloud Gateway的过滤器链是如何工作的?如何实现统一鉴权?
【标准答案】 Spring Cloud Gateway的过滤器链基于WebFlux的响应式编程模型,核心是GatewayFilterChain,每个过滤器可以对请求进行预处理和后处理。
过滤器类型:
- GatewayFilter:网关过滤器,作用在单个路由或路由组上
- GlobalFilter:全局过滤器,作用在所有路由上
执行流程: 请求进来 → DispatcherHandler → RoutePredicateHandlerMapping匹配路由 → FilteringWebHandler加载过滤器链 → 按order排序依次执行 → 每个过滤器的filter()方法处理请求 → 调用chain.filter(exchange).then()传递给下一个过滤器 → 最后转发到后端服务 → 响应返回时按相反顺序执行后处理逻辑。
统一鉴权实现:
@Component
public class AuthFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getHeaders().getFirst("Authorization");
if (StringUtils.isEmpty(token)) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
// 验证token,解析用户信息
Claims claims = JwtUtil.parseToken(token);
if (claims == null) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
// 将用户信息透传给下游服务
ServerHttpRequest request = exchange.getRequest().mutate()
.header("X-User-Id", claims.get("userId").toString())
.build();
return chain.filter(exchange.mutate().request(request).build());
}
@Override
public int getOrder() {
return -100; // 优先级高,先执行
}
}生产要点:过滤器顺序很重要,鉴权要靠前;响应式编程不要阻塞;白名单路径要跳过鉴权;Token解析失败要返回标准错误码;用户信息透传给下游服务避免重复解析。
【面试题】6. OpenFeign的超时重试如何配置?有哪些坑需要注意?
【标准答案】 OpenFeign的超时分两层:connectTimeout(连接超时)和readTimeout(读取超时)。重试通过Retryer实现。
配置方式:
feign:
client:
config:
default:
connectTimeout: 5000 # 连接超时5秒
readTimeout: 10000 # 读取超时10秒
specific-service:
connectTimeout: 3000
readTimeout: 5000重试配置:
@Configuration
public class FeignConfig {
@Bean
public Retryer retryer() {
// period: 重试间隔,maxAttempts: 最大重试次数(包含首次调用)
return new Retryer.Default(100, TimeUnit.SECONDS.toMillis(1), 3);
}
}常见坑:
默认重试:Feign默认有重试机制,
Retryer.Default会重试5次,生产环境需要自定义Retryer或用Retryer.NEVER_RETRY关闭。超时时间叠加:如果设置了重试3次,每次超时10秒,最坏情况下会等待30秒,要配合接口的超时设置。
Hystrix/Sentinel冲突:如果同时用了熔断器,熔断的超时要小于Feign的超时,否则熔断不生效。比如Feign读超时10秒,熔断超时要设8秒。
GET幂等性问题:Feign默认只对GET请求重试,POST请求不会重试。如果POST需要重试,要自定义
Retryer,但要确保接口幂等。超时穿透:Feign超时后,后端服务可能还在执行,要考虑是否需要取消请求或做资源清理。
最佳实践:生产环境一般关闭Feign重试,交给上游(如网关或服务调用方)统一处理;如果开重试,必须确保接口幂等;超时时间根据P99响应时间设置,宁可快速失败也不要长时间等待。
【面试题】7. SkyWalking链路追踪的采样策略有哪些?对性能有什么影响?
【标准答案】 SkyWalking采样策略决定哪些请求会被追踪,主要三种:
1. 全量采样(默认)
- 配置:
agent.sample_n_per_3_secs = -1或不配置 - 所有请求都采集,链路完整,便于问题排查
- 缺点:数据量大,存储压力大,对性能有影响(每次请求产生约0.5-1ms开销)
2. 固定比例采样
- 配置:
agent.sample_n_per_3_secs = 9,表示每3秒采样9个请求 - 适合高并发场景,控制数据量
- 缺点:可能漏掉关键链路,问题复现难
3. 动态采样(推荐)
- 通过SkyWalking后台配置采样率,支持动态调整
- 可按服务、接口、时间维度设置不同采样率
- 核心接口100%采样,非核心接口10%采样
性能影响:
- CPU开销:每个请求生成TraceId、SpanId,收集调用链信息,约占请求处理时间的1-3%
- 内存开销:Agent本地缓存调用链数据,批量上报,约占用10-50MB堆内存
- 网络开销:调用链数据上报到OAP Server,带宽占用约为请求量的5-10%
- 存储开销:Elasticsearch存储,数据量大时需要考虑分片和保留策略
优化建议:
- 生产环境用动态采样,核心服务100%,非核心服务10-30%
- 调整上报频率:
agent.span_limit_per_segment = 300,限制单个链路Span数量 - 忽略不需要追踪的接口:
trace.ignore_path = /health,/metrics - 使用异步上报:
is_plugin_apm_toolkit_trace_active = true - 合理设置数据保留周期:一般保留7-15天,问题排查时临时调大
监控策略:SkyWalking本身也会产生性能开销,建议单独部署Agent,不要和业务服务抢占资源;同时监控SkyWalking自身的性能指标(OAP Server的吞吐量、ES的写入延迟)。
【面试题】8. Eureka的自我保护机制是什么?生产环境应该如何配置?
【标准答案】 Eureka自我保护机制是为了防止网络分区故障导致的"误杀"——当Eureka Server短时间内丢失大量客户端心跳,不会立即剔除这些服务,而是进入自我保护模式,保留所有注册信息。
触发条件:当每分钟期望续约数(numberOfRenewsPerMinThreshold)低于实际续约数的85%时触发。计算公式:
- 期望续约数 = 服务数 × 2(每个服务每分钟发2次心跳)× 续约百分比阈值(默认0.85)
自我保护模式行为:
- 不再剔除注册表中任何服务实例
- 继续接受新服务注册
- 继续提供服务查询
- 控制台显示红色警告
生产配置建议:
开发/测试环境:关闭自我保护,方便调试
eureka:
server:
enable-self-preservation: false
eviction-interval-timer-in-ms: 5000 # 5秒清理一次失效实例生产环境:开启自我保护,但要调整参数
eureka:
server:
enable-self-preservation: true
renewal-percent-threshold: 0.75 # 降低阈值,减少误触发
eviction-interval-timer-in-ms: 60000 # 60秒清理一次
peer-eureka-nodes-update-interval-ms: 300000 # 集群节点同步间隔
instance:
lease-renewal-interval-in-seconds: 30 # 心跳间隔30秒
lease-expiration-duration-in-seconds: 90 # 过期时间90秒最佳实践:
- 生产必须开启自我保护,但建议阈值设为0.6-0.75,避免频繁进入保护模式
- 配合健康检查:
eureka.client.healthcheck.enabled=true,让Eureka感知服务真实状态 - 部署Eureka集群(至少2个节点),避免单点故障
- 监控自我保护状态,频繁触发说明网络或服务有问题
- 考虑迁移到Nacos:Eureka已停止维护,Nacos支持健康检查更灵活,自我保护机制更完善
【面试题】9. RocketMQ如何实现顺序消息?有哪些使用场景和注意事项?
【标准答案】 RocketMQ顺序消息分两种:全局有序和分区有序。
全局有序:整个Topic下所有消息严格按FIFO顺序消费。实现方式是Topic只建一个Queue,所有消息发到同一个Queue。缺点是并发度低,吞吐量受限,极少使用。
分区有序(常用):同一业务ID的消息发送到同一个Queue,由同一个消费者处理,保证局部有序。
实现方式:
// 生产者:发送顺序消息,指定MessageQueueSelector
Message msg = new Message("TopicTest", "TagA", "OrderID001", body);
SendResult result = producer.send(msg, new MessageQueueSelector() {
@Override
public MessageQueue select(List<MessageQueue> mqs, Message msg, Object arg) {
Long orderId = (Long) arg; // 根据订单ID选择队列
long index = orderId % mqs.size();
return mqs.get((int)index);
}
}, orderId); // orderId作为选择key消费者顺序消费:
consumer.registerMessageListener(new MessageListenerOrderly() {
@Override
public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
// 顺序消费,同一个Queue的消息会被加锁
for (MessageExt msg : msgs) {
// 处理消息
}
return ConsumeOrderlyStatus.SUCCESS;
}
});典型场景:
- 订单状态流转:创建→支付→发货→完成,必须按顺序
- 数据库binlog同步:保证SQL执行顺序
- 充值回调:同一用户的充值请求需顺序处理
注意事项:
- 选择Key要合理:用业务唯一标识(订单ID、用户ID)作为分区Key,避免数据倾斜
- 消费失败处理:顺序消费失败会阻塞后续消息,必须设置合理的重试次数和死信处理
- 消费者扩容:分区有序场景下,消费者数量不能超过Queue数量,否则部分消费者空闲
- 性能权衡:顺序消息牺牲并发性,Queue越多并发越高,但同一Key的顺序保证越弱
- 异常处理:消费者宕机时,Broker会重新分配Queue,期间可能短暂乱序
最佳实践:业务层面尽量做到幂等,即使短暂乱序也能通过状态机校验兜底;消费失败时快速失败,不要无限重试阻塞队列。
【面试题】10. Spring Cloud Kubernetes与Spring Cloud传统组件有什么区别?什么场景下选择Kubernetes方案?
【标准答案】 Spring Cloud Kubernetes是用Kubernetes原生能力替代Spring Cloud传统组件的方案。
核心组件对比:
| 功能 | Spring Cloud传统组件 | Spring Cloud Kubernetes |
|---|---|---|
| 服务注册发现 | Eureka/Nacos | K8s Service + DNS |
| 配置中心 | Spring Cloud Config/Nacos | K8s ConfigMap/Secret |
| 负载均衡 | Ribbon/LoadBalancer | K8s Service负载均衡 |
| 熔断限流 | Hystrix/Sentinel | 可选,或用Istio |
| API网关 | Spring Cloud Gateway | Ingress/Istio Gateway |
Spring Cloud Kubernetes优势:
- 原生集成:直接用K8s的Service发现,无需额外部署注册中心
- 配置热更新:ConfigMap/Secret变更自动注入Pod,无需重启
- 运维简化:减少中间件,降低运维复杂度
- 资源效率:无需Eureka、Config Server等组件,节省资源
- 云原生标准:符合K8s生态,便于接入Istio等Service Mesh
Spring Cloud Kubernetes劣势:
- 强绑定K8s:无法在非K8s环境运行,开发调试麻烦
- 功能受限:K8s Service只支持轮询,无法实现加权、一致性哈希等高级策略
- 无熔断限流:K8s本身不提供熔断,需要配合Istio或保留Sentinel
- 配置管理弱:ConfigMap缺乏Nacos那样的分组、命名空间管理能力
- 链路追踪缺失:需要单独集成SkyWalking或Istio
选择建议:
选Spring Cloud Kubernetes:
- 已全面上K8s,且不打算用其他编排平台
- 团队熟悉K8s生态,想减少中间件运维
- 对负载均衡策略要求不高,简单的轮询即可
- 准备引入Service Mesh(Istio),逐步替换Spring Cloud组件
选Spring Cloud传统组件:
- 混合部署(部分K8s,部分物理机/虚拟机)
- 需要复杂的流量治理(灰度发布、流量镜像、AB测试)
- 配置管理需求复杂(多环境、多租户、动态推送)
- 团队对Spring Cloud生态更熟悉,K8s能力不足
混合方案(推荐):
- 服务发现用K8s Service
- 配置中心用Nacos(功能更强)
- 熔断限流用Sentinel
- API网关用Spring Cloud Gateway
这样既利用了K8s原生能力,又保留了Spring Cloud的灵活性。核心原则:K8s负责调度和基础服务发现,Spring Cloud负责业务层治理。