微服务题的重点不是组件清单,而是「拆不拆、怎么通、出错怎么办」。下面按这三个问题展开,组件只作为实现手段出现。
1. 微服务应该怎么拆?什么时候不该拆?
常见依据是业务能力与限界上下文:一个服务应拥有相对完整的一块业务和它的数据,能独立部署、独立演进。也可以按组织结构和变更频率拆,让经常一起改的代码待在一起。
判断边界时问三个问题:这块能力是否有独立的生命周期?是否有清晰的数据所有权?跨边界的调用是否足够少?如果一个变更总要同时改五个服务,说明边界切错了。
不该拆的情况同样重要:团队规模小、领域还没稳定、强事务要求高、流量很低。此时模块化单体往往更划算,等边界清晰后再拆,比一开始拆错再合并容易。
还要警惕「按技术分层拆」:用户服务、订单服务之外再拆出「查询服务」「写入服务」,会把一次业务变更拉成跨团队协作,收益通常抵不过成本。
2. Spring Cloud 常见组件分别解决什么问题?
网关负责统一入口、路由、鉴权、限流和灰度;注册中心负责服务实例的注册与发现,常见有 Nacos、Consul、Eureka;客户端负载均衡负责在实例列表中选择目标,Spring Cloud LoadBalancer 已替代 Ribbon。
声明式调用用 OpenFeign,把 HTTP 调用封装成接口;容错限流可以用 Sentinel 或 Resilience4j;配置中心负责动态配置;链路追踪在 Spring Boot 3 时代通常用 Micrometer Tracing 对接 OpenTelemetry。
要注意版本配套:Spring Boot 3 / Spring Cloud 2022+ 使用 Jakarta 命名空间,旧版 Sleuth 已不再是主线。选组件时优先看团队是否真的会用,而不是把全家桶一次性引入。
组件越多,故障面越大。一个只有三个服务的小系统,可能只需要网关、注册中心和基础监控。
3. 服务注册发现和负载均衡是怎么工作的?
服务启动时把地址、端口、元数据注册到注册中心,并定期发送心跳;消费者拉取或订阅实例列表,缓存在本地,调用时按负载均衡策略选一个实例。
注册中心通常区分临时实例和持久实例:临时实例靠心跳续约,失联后被剔除;持久实例需要显式注销。健康检查有客户端心跳和服务端探测两种方式。
选型会涉及 CAP:Eureka 偏向可用性,允许短时间读到旧列表;ZooKeeper 偏向一致性,网络分区时可能拒绝写;Nacos 默认偏 AP,也提供 CP 模式。
客户端负载均衡不需要额外的代理进程,但每个消费者都要维护实例列表和健康判断。服务端负载均衡(如 Nginx、云负载均衡)把选择集中在一处,运维简单但多一跳。
4. Dubbo 和 OpenFeign 有什么区别?SPI 有什么不同?
OpenFeign 基于 HTTP,语义通用、调试方便、跨语言友好,适合对外接口和异构系统;代价是文本协议和每次调用的连接、序列化开销相对高。
Dubbo 是 RPC 框架,默认走二进制协议和 TCP 长连接,性能通常更好,提供服务治理能力(超时、重试、路由、负载均衡、熔断)。它更适合内部高频调用,但要求双方使用兼容的协议和序列化方式。
Dubbo 3 支持 Triple 协议,兼容 gRPC 和 HTTP/2,跨语言能力比早期版本好很多。选型不能只看性能,还要看团队对协议、序列化和治理的掌握程度。
SPI 方面,JDK SPI 通过 META-INF/services 加载,一次性实例化所有实现,无法按需选择;Dubbo SPI 用 @SPI 指定默认实现,@Adaptive 做自适应扩展,@Activate 做条件激活,支持按名称获取和包装扩展,灵活得多。
5. 超时、重试、熔断、限流应该怎么配?
超时应该自下而上设置:下游超时小于上游,并留出处理余量。更好的做法是传递截止时间(deadline),让整条调用链共享同一个预算,而不是每层各自设一个固定值。
重试只对幂等操作开启,并且要限制次数、加退避和抖动。同步调用链上层层重试会造成放大效应,三层各重试两次就是八倍流量,容易把已经过载的下游彻底压垮。
熔断器有闭合、打开、半开三个状态:失败率超阈值后打开,快速失败;经过冷却期进入半开,放少量请求试探。它保护的是调用方,也给了下游恢复时间。
限流常用令牌桶(允许突发)和漏桶(平滑输出),还要考虑单机限流与集群限流的差别。降级方案要提前设计好返回值,不能等故障时临时决定。
6. 分布式事务有哪些方案?Seata AT 的边界在哪?
强一致方案主要是 2PC/XA:协调者统一提交或回滚,实现简单但有同步阻塞、协调者单点和性能问题。3PC 缓解阻塞,仍不能很好应对网络分区。
柔性事务包括 TCC、Saga、本地消息表和可靠消息。TCC 需要业务实现 Try/Confirm/Cancel,要处理空回滚、悬挂和幂等;Saga 用一系列本地事务加补偿,隔离性弱,适合长流程;本地消息表靠最终一致,工程上最常用。
Seata AT 通过解析 SQL 生成前后镜像,提交本地事务并记录 undo log,失败时反向补偿。它默认不提供强隔离,读到中间状态需要配合全局锁或业务设计;复杂 SQL、存储过程、非事务表都可能失效。
选择方案的顺序应该是:能不能不分布式事务(合并服务、调整边界)→ 能否最终一致 → 最后才考虑强一致方案。
7. 微服务可观测性要建设哪些能力?
三根支柱是日志、指标和链路追踪。日志要结构化,并带上 traceId、用户标识和业务单号;指标关注流量、错误率、延迟和饱和度;链路追踪要能串起跨服务调用,定位是哪一跳变慢。
指标命名可以套用 RED(请求速率、错误、耗时)和 USE(使用率、饱和度、错误);告警要基于症状(用户可感知的错误率和延迟),而不是只看 CPU。
OpenTelemetry 正在成为事实标准,trace context 通过 W3C traceparent 头传递。采样策略要平衡成本与可观测性,错误请求和慢请求应提高采样率。
可观测性不是装个 APM 就结束:要有统一的检索入口、清晰的仪表盘和可执行的 Runbook,否则故障时仍然靠猜。
8. 网关层要做哪些事?鉴权怎么设计?
网关适合做与业务无关的横切能力:路由转发、TLS 终止、统一鉴权、限流熔断、跨域、请求日志、灰度分流和协议转换。它不适合承载业务逻辑,否则会成为新的单体。
鉴权可以在网关做粗粒度的身份校验(验签、解析 token、拦截未登录请求),细粒度的权限判断仍应放在服务内,因为服务需要知道「这个用户能否操作这个资源」。
JWT 自带声明、便于水平扩展,但撤销困难;常见做法是短有效期加刷新令牌,敏感操作再查一次权限或黑名单。传统 Session 依赖集中存储,撤销容易但需要共享会话。
内部服务之间建议使用 mTLS 或服务身份令牌,不要默认「内网可信」。网关还要防止把外部请求头原样透传,避免伪造身份字段。
易错点与自测
- 拆分不是越细越好,数据所有权和变更边界比「服务数量」更重要。
- 重试是放大器,没有幂等和退避的重试是故障加速器。
- 分布式事务没有银弹,先问业务能否接受最终一致。
自测:① 说明客户端负载均衡与服务端负载均衡的差异;② 画出熔断器的三个状态及迁移条件;③ 解释 Seata AT 为什么默认不是强隔离。
参考资料
- Spring Cloud 官方文档 — 各组件与版本对应关系
- Apache Dubbo 官方文档 — 协议、SPI 与服务治理
- Seata 官方文档 — AT、TCC、Saga 的模式与约束
- OpenTelemetry 文档 — 链路、指标与日志的统一采集