微服务面试题
1. 微服务和单体架构的区别?
| 特性 | 单体架构 | 微服务架构 |
|---|---|---|
| 部署 | 整体打包部署 | 独立部署 |
| 扩展 | 整体扩展 | 按需扩展 |
| 技术栈 | 统一 | 可异构 |
| 数据库 | 共享数据库 | 每个服务独有数据库 |
| 复杂度 | 代码耦合 | 分布式系统复杂性 |
| 通信 | 本地调用 | 网络通信 |
2. Spring Cloud 微服务有哪些核心模块?各自的作用是什么?
Spring Cloud 是微服务架构的一站式解决方案,提供了服务治理、配置管理、负载均衡、熔断降级、链路追踪等一系列组件。以下是核心模块及其作用:
2.1 服务注册与发现 —— Spring Cloud Netflix Eureka / Nacos Discovery
作用: 解决微服务架构中"服务在哪里"的问题。在微服务架构中,服务实例的数量和位置是动态变化的,服务注册与发现让服务之间能够自动找到彼此,而不需要硬编码 IP 地址。
Eureka 核心机制:
- 服务注册:服务启动时向 Eureka Server 注册自己的元数据(服务名、IP、端口、健康状态等)
- 心跳续约:服务每 30 秒发送一次心跳,证明自己还活着。Eureka Server 90 秒未收到心跳则剔除该实例
- 服务发现:服务调用方从 Eureka Server 拉取服务注册表,并缓存在本地,定时更新
- 自我保护模式:当 Eureka Server 短时间内丢失大量心跳时,自动进入自我保护模式,不再剔除任何实例,防止因网络抖动导致大面积误删
Nacos 相比 Eureka 的优势:
- 支持 AP/CP 模式切换,灵活应对不同场景
- 不仅做注册发现,还做配置中心,功能合一
- 心跳 + 主动健康检查,更可靠
- 阿里持续维护,生态活跃
面试要点: 区分 Eureka 的 AP 模型(优先保证可用性,可能返回过期实例)和 Zookeeper 的 CP 模型(优先保证一致性,Leader 故障时不可用)。
2.2 配置中心 —— Spring Cloud Config / Nacos Config
作用: 解决微服务架构中"配置在哪里"的问题。几十个微服务的配置如果散落在各自的配置文件中,修改一个配置需要重新部署大量服务,配置管理成为噩梦。配置中心实现配置的集中管理、动态刷新、环境隔离。
核心功能:
- 集中管理:所有服务的配置统一存储在配置中心,一处修改,全局生效
- 动态刷新:配置变更后无需重启服务,通过
@RefreshScope注解或 Nacos 的监听机制实现热更新 - 环境隔离:通过 namespace 或 profile 实现 dev / test / prod 环境配置隔离
- 版本管理:配置变更历史可追溯,支持回滚
- 灰度发布:部分实例先使用新配置,验证无问题后全量推送
面试要点: 配置刷新的实现原理(长轮询 vs WebSocket 推送 vs Spring Cloud Bus 广播),以及为什么需要配置中心(想象一下 100 个服务每个都要改 Redis 地址的场景)。
2.3 API 网关 —— Spring Cloud Gateway
作用: 解决微服务架构中"请求从哪里进"的问题。网关是整个系统的唯一入口,所有外部请求都经过网关,由网关路由到对应的微服务。
为什么需要网关?
- 如果让前端直接调用各个微服务,前端需要知道所有服务的地址,且每个服务都要自己处理鉴权、限流、日志、跨域,代码重复严重
- 网关将这些通用能力统一收敛,实现了"关注点分离"
Spring Cloud Gateway 三大核心组件:
| 组件 | 作用 | 示例 |
|---|---|---|
| Route(路由) | 定义请求的转发规则 | /order/** → order-service |
| Predicate(断言) | 匹配请求的条件 | Path、Header、Method、Query 参数等 |
| Filter(过滤器) | 对请求/响应进行拦截处理 | 鉴权、限流、日志、修改请求头 |
与 Zuul 的对比:
- Gateway 基于 WebFlux + Netty,非阻塞异步,性能远高于 Zuul 1.x(基于 Servlet 阻塞模型)
- Gateway 使用函数式编程定义路由规则,比 Zuul 的 Filter 链更灵活
- Zuul 2.x 虽然也改为非阻塞,但 Spring 官方已明确推荐 Gateway
面试要点: 网关不是银弹,它会成为单点瓶颈和单点故障,需要集群部署保证高可用。另外,网关层不应该承载过重的业务逻辑,否则网关本身就成了新的"单体"。
2.4 声明式服务调用 —— Spring Cloud OpenFeign
作用: 解决微服务架构中"服务怎么调用"的问题。让服务间的 HTTP 调用像调用本地方法一样简单,通过接口 + 注解的方式声明远程调用。
核心特性:
- 声明式调用:只需定义接口 +
@FeignClient注解,无需写 HTTP 调用代码 - 负载均衡集成:内置集成 Ribbon 或 Spring Cloud LoadBalancer
- 熔断降级集成:配合 Sentinel 或 Hystrix 实现容错
- 请求拦截器:统一添加请求头(如 Token 传递、TraceId 透传)
- 编解码器:支持 JSON、XML、表单等多种格式
工作流程:
- 定义
@FeignClient(name = "order-service")接口 - Feign 根据接口生成动态代理对象
- 调用接口方法时,代理对象根据服务名去注册中心查询实例列表
- 通过负载均衡选择一个实例
- 构造 HTTP 请求发送到目标服务
- 解析响应,返回结果
面试要点: Feign 的本质是动态代理 + HTTP 客户端。需要理解 @FeignClient 的配置项(超时、重试、日志级别),以及如何通过 RequestInterceptor 实现链路追踪的 TraceId 透传。
2.5 负载均衡 —— Spring Cloud LoadBalancer
作用: 解决"选哪个实例调用"的问题。当一个服务有多个实例时,负载均衡器决定将请求发给哪个实例。
与 Ribbon 的关系:
- Ribbon 是 Netflix 开源的客户端负载均衡器,已进入维护模式
- Spring Cloud LoadBalancer 是 Spring 官方替代品,更轻量,与 Spring 生态集成更好
常见负载均衡策略:
- 轮询(Round Robin):依次分配,最简单的策略
- 加权轮询(Weighted):根据服务器性能分配不同权重
- 随机(Random):随机选择
- 最小活跃数(Least Active):选择当前处理请求最少的实例
- 一致性哈希(Consistent Hash):相同参数的请求总是路由到同一实例,适合有状态场景
面试要点: 区分客户端负载均衡(Ribbon / LoadBalancer,调用方自己选实例)和服务端负载均衡(Nginx,请求先到 LB 再转发)。微服务架构中通常使用客户端负载均衡,因为它更灵活,去掉了中心化 LB 的瓶颈。
2.6 熔断降级 —— Sentinel / Resilience4j
作用: 解决微服务架构中"一个服务挂了怎么办"的问题。在分布式系统中,一个服务的故障可能像雪崩一样蔓延到整个系统,熔断降级就是为了阻断这种级联故障。
Sentinel 核心能力:
| 能力 | 说明 | 场景举例 |
|---|---|---|
| 流量控制 | QPS / 并发线程数限制 | 秒杀场景限制下单接口 QPS |
| 熔断降级 | 慢调用比例 / 异常比例超过阈值,自动熔断 | 数据库挂了,快速返回降级结果 |
| 系统保护 | 根据系统 Load、CPU 使用率自动限流 | 防止系统被突发流量打垮 |
| 热点参数限流 | 针对特定参数值限流 | 限制单个商品 ID 的查询频率 |
| 规则持久化 | 规则存储在 Nacos / Apollo 等配置中心 | 重启后规则不丢失 |
熔断器状态机:
- 关闭(Closed):正常状态,请求正常通过
- 打开(Open):失败次数达到阈值,熔断器打开,所有请求直接失败
- 半开(Half-Open):经过一段时间后,尝试放行一个请求,如果成功则关闭熔断器,如果失败则继续保持打开状态
Sentinel vs Hystrix:
- Hystrix 已停止维护,官方推荐迁移到 Resilience4j 或 Sentinel
- Sentinel 控制台可视化更好,支持实时监控和规则动态调整
- Sentinel 支持更细粒度的流量控制(QPS、线程数、关联资源、链路)
面试要点: 熔断和降级是两个概念:熔断是自动的(根据失败率自动触发),降级是主动的(提前定义好失败时的兜底逻辑)。实际开发中,熔断和降级通常配合使用:熔断触发后,走降级逻辑返回兜底结果。
2.7 链路追踪 —— Micrometer Tracing(原 Spring Cloud Sleuth)
作用: 解决微服务架构中"请求经过了哪些服务"的问题。一个用户请求可能经过网关 → 订单服务 → 库存服务 → 支付服务 → 物流服务,没有链路追踪,排查问题如大海捞针。
核心概念:
| 概念 | 说明 |
|---|---|
| TraceId | 一次完整请求链路的唯一标识,贯穿所有服务 |
| SpanId | 链路中每个操作单元的唯一标识 |
| ParentSpanId | 父 Span 的 ID,用于构建调用树 |
工作原理:
- 请求进入第一个服务时,生成全局唯一的 TraceId 和 SpanId
- 调用下游服务时,将 TraceId 和 SpanId 放入请求头透传
- 下游服务解析请求头,创建新的 Span,ParentSpanId 指向上游的 SpanId
- 所有 Span 上报到追踪系统(如 Zipkin / SkyWalking / Jaeger)
- 通过 TraceId 查询完整调用链
面试要点: 日志中打印 TraceId 是排查问题的关键手段。通过 %X{traceId} 在 Logback 配置中输出 TraceId,出问题时只需要用户提供一个 TraceId,就能串联出整个调用链的所有日志。
2.8 消息驱动 —— Spring Cloud Stream
作用: 解耦微服务之间的异步通信,屏蔽底层消息中间件的差异。开发者只需面向统一的编程模型编程,无需关心底层是 Kafka、RocketMQ 还是 RabbitMQ。
核心概念:
- Binder:适配层,屏蔽不同消息中间件的差异
- Channel:消息通道,分为 Input(消费)和 Output(生产)
- Message:消息体,包含 payload 和 header
适用场景:
- 异步解耦:下单后异步发送短信、推送通知
- 流量削峰:秒杀请求先写入 MQ,后端慢慢消费
- 数据同步:数据变更后通过 MQ 通知其他服务同步
2.9 消息总线 —— Spring Cloud Bus
作用: 实现微服务之间的广播通信,通常配合 Spring Cloud Config 实现配置的批量刷新。
工作原理:
- 基于消息中间件(RabbitMQ / Kafka)实现
- 当配置中心的配置变更时,通过 Bus 广播 Refresh 事件
- 所有订阅了该事件的服务自动刷新配置
与 Nacos 的对比:
- Nacos 自带配置推送能力,不需要额外引入 Bus
- 如果使用 Spring Cloud Config + Git 作为配置中心,则需要 Bus 来实现配置的批量刷新
2.10 模块体系总结
┌─────────────────────────────────────────────────────┐
│ 外部请求 │
└─────────────────────┬───────────────────────────────┘
│
┌───────────▼───────────┐
│ Gateway (API 网关) │ ← 统一入口、鉴权、限流
└───────────┬───────────┘
│
┌───────────▼───────────┐
│ Nacos (注册+配置) │ ← 服务发现、配置管理
└───────────┬───────────┘
│
┌─────────┬───────┼───────┬─────────┐
│ │ │ │ │
┌───▼───┐ ┌───▼───┐ ┌─▼──┐ ┌──▼───┐ ┌──▼───┐
│ 订单 │ │ 用户 │ │商品│ │ 支付 │ │ 物流 │
│ 服务 │ │ 服务 │ │服务│ │ 服务 │ │ 服务 │
└───┬───┘ └───┬───┘ └─┬──┘ └──┬───┘ └──┬───┘
│ │ │ │ │
│ Feign (声明式调用) + LoadBalancer (负载均衡) │
│ │ │ │ │
│ Sentinel (熔断降级限流) │ │
│ │ │ │ │
│ Sleuth/Micrometer Tracing (链路追踪) │ │
│ │ │ │ │
└─────────┴───────┼───────┴─────────┘
│
┌───────────▼───────────┐
│ Stream (消息驱动) │ ← 异步解耦
└───────────┬───────────┘
│
┌───────────▼───────────┐
│ Bus (消息总线) │ ← 配置广播
└───────────────────────┘面试回答模板:
"Spring Cloud 是一套微服务治理的完整解决方案。我实际项目中使用过以下核心模块:
Nacos 做服务注册发现和配置中心,解决服务之间怎么找到彼此、配置怎么集中管理的问题; Gateway 做 API 网关,作为系统统一入口,处理路由转发、鉴权、限流; OpenFeign + LoadBalancer 做服务间调用,Feign 让远程调用像本地调用一样简单,LoadBalancer 负责负载均衡; Sentinel 做熔断降级和流量控制,防止级联故障和突发流量打垮系统; Sleuth/Micrometer Tracing 做链路追踪,配合 SkyWalking 或 Zipkin 可视化调用链,方便排查问题; 异步场景下用 Stream 对接消息队列,解耦服务间的同步依赖。
这些组件各司其职,共同支撑起一个完整的微服务体系。选型上,我们现在更倾向于 Spring Cloud Alibaba 生态(Nacos + Sentinel),因为它们功能更全、社区更活跃,而且阿里有大规模生产验证。"
3. 服务注册与发现的原理?
- 服务注册:服务启动时向注册中心注册自己的信息(IP、端口、服务名)
- 心跳续约:定时发送心跳,保持注册信息有效
- 服务发现:调用方从注册中心获取服务实例列表
- 负载均衡:调用方通过负载均衡策略选择实例
- 服务下线:服务关闭或无心跳时,注册中心移除实例
4. Eureka 和 Nacos 的区别?
| 特性 | Eureka | Nacos |
|---|---|---|
| CAP | AP | AP/CP 可切换 |
| 功能 | 仅注册发现 | 注册发现 + 配置中心 |
| 健康检查 | 心跳 | 心跳 + 主动探测 |
| 一致性协议 | 无 | Raft(CP 模式) |
| 维护状态 | 停止维护 | 持续更新 |
5. Spring Cloud Gateway 的原理?
Spring Cloud Gateway 基于 WebFlux 框架,使用 Netty 作为底层通信。
三大核心组件:
- Route(路由):包含 ID、目标 URI、Predicate 集合、Filter 集合
- Predicate(断言):匹配请求的条件(路径、Header、参数等)
- Filter(过滤器):对请求进行修改或拦截
6. 分布式事务如何解决?
| 方案 | 原理 | 适用场景 |
|---|---|---|
| 2PC | 两阶段提交,协调者统一管理 | 强一致性要求 |
| TCC | Try-Confirm-Cancel 三阶段 | 资金交易等 |
| 可靠消息 | 本地消息表 + 消息队列保证最终一致 | 高并发场景 |
| 最大努力通知 | 反复通知直到成功 | 短信通知等 |
| Seata | AT 模式:一阶段提交 + 二阶段回滚 | 通用场景 |
7. CAP 定理是什么?如何选择?
CAP:一致性(Consistency)、可用性(Availability)、分区容错性(Partition Tolerance),三者只能同时满足两个。
- CP:保证一致性和分区容错,牺牲可用性(Zookeeper、Consul)
- AP:保证可用性和分区容错,牺牲一致性(Eureka、Nacos AP 模式)
- 分区容错性必须保证,实际是在 CP 和 AP 之间选择
8. 如何保证接口幂等性?
- 数据库唯一索引:利用唯一约束防止重复插入
- Token 机制:请求前获取 Token,提交时校验 Token
- 状态机:通过状态流转保证幂等
- 分布式锁:Redis / Zookeeper 锁防止并发重复请求
- 乐观锁:版本号机制,更新时校验版本