Skip to content

微服务架构 Microservices Architecture

概念

微服务架构将单体应用拆分为一组小型、独立部署的服务,每个服务围绕特定业务能力构建,拥有独立的进程和数据存储,通过轻量级通信机制(HTTP / gRPC / 消息队列)相互协作。

单体架构的演进困境

┌─────────────────────────────────────────┐
│              单体应用 (Monolith)           │
│                                           │
│  ┌────────┐ ┌────────┐ ┌────────┐        │
│  │用户模块 │ │订单模块 │ │支付模块 │        │
│  └───┬────┘ └───┬────┘ └───┬────┘        │
│      │          │          │              │
│  ┌───┴──────────┴──────────┴───┐         │
│  │       共享数据库              │         │
│  └─────────────────────────────┘         │
│                                           │
│  打包为一个 WAR/JAR → 部署到一台服务器      │
└─────────────────────────────────────────┘

当团队和业务规模增长后,单体架构会遇到以下问题:

  • 部署耦合: 修改一行代码需要重新部署整个应用
  • 技术栈绑定: 所有模块必须使用相同的语言和框架
  • 扩展受限: 无法对某个模块单独扩容,只能整体扩容
  • 协作冲突: 多人开发同一代码库,分支合并冲突频繁

核心原理

1. 三大核心原则

原则含义
单一职责每个服务只负责一个明确的业务能力(用户、订单、支付各自独立)
去中心化各团队自主选技术栈;每个服务拥有独立数据库,不共享底层存储
独立部署每个服务可独立开发、测试、部署、扩缩容,无需协调其他服务

2. 服务拆分原则

DDD 限界上下文映射

领域驱动设计(DDD)是指导服务拆分的核心方法论:以**限界上下文(Bounded Context)**为边界,每个限界上下文对应一个微服务。

电商系统限界上下文划分:

┌──────────────┐  ┌──────────────┐  ┌──────────────┐
│   用户上下文   │  │   订单上下文   │  │   支付上下文   │
│              │  │              │  │              │
│ User         │  │ Order        │  │ Payment      │
│ Profile      │  │ OrderItem    │  │ Transaction  │
│ Address      │  │ Discount     │  │ Refund       │
└──────────────┘  └──────────────┘  └──────────────┘
       ↕ 通过 API 交互,不共享数据库
┌──────────────┐  ┌──────────────┐
│   库存上下文   │  │   物流上下文   │
│              │  │              │
│ Product      │  │ Shipment     │
│ SKU          │  │ Carrier      │
│ Stock        │  │ Tracking     │
└──────────────┘  └──────────────┘

拆分信号(何时应该拆分)

  • 团队规模增长: 单个团队超过 8-10 人,沟通协调成本急剧上升
  • 独立发布需求: 某个模块需要高频独立上线,但受其他模块耦合阻碍
  • 差异化扩容: 某个功能(如搜索、推荐)需要独立扩容,其他模块不需要
  • 技术异构需求: 特定功能需要用不同语言或框架实现

常见反模式

  • 按技术层拆分(错误): 把 Controller 层、Service 层、DAO 层各自拆成一个服务——这只是分布式单体,并非微服务
  • 拆分粒度过细: 每个方法一个服务,导致服务间调用比本地调用还多,网络开销和分布式事务成本远高于收益

迁移路径:绞杀者模式(Strangler Fig Pattern)

阶段一:                    阶段二:                    阶段三:
单体应用                    引入代理层                  逐步迁移

┌──────────┐              ┌──────────┐              ┌──────────┐
│          │              │  Proxy   │              │  Proxy   │
│  Monolith│              │ /users → │──新服务→      │ /users → │──用户服务
│  - 用户   │    →         │ /orders→ │──原单体→      │ /orders→ │──订单服务
│  - 订单   │              │ /pay  → │──原单体→      │ /pay  → │──支付服务
│  - 支付   │              └──────────┘              └──────────┘
└──────────┘              单体仍然运行                单体逐渐缩小直至废弃

核心思路: 新功能全部在新服务中开发,用代理层(API 网关)路由请求,逐步将旧功能从单体迁移到新服务,单体像被绞杀的植物一样逐渐萎缩。

3. 服务通信

通信方式对比

特性REST (HTTP/JSON)gRPC (HTTP/2 + Protobuf)消息队列
通信方式同步请求/响应同步请求/响应异步消息
序列化JSON(文本)Protobuf(二进制)自定义
性能一般高(二进制传输、多路复用)取决于 MQ
跨语言好(HTTP 标准)好(.proto 生成多语言代码)
流式传输不原生支持支持(双向流、服务端流、客户端流)天然支持
浏览器兼容需要 gRPC-Web 代理不直接支持
适用场景对外 API、前后端通信服务间高性能内部通信异步解耦、事件驱动

选型建议:

  • 对外 API / 前端调用 → REST
  • 内部服务间高频调用 → gRPC
  • 异步、事件驱动的场景 → 消息队列

REST 示例

java
// Spring Boot REST Controller
@RestController
@RequestMapping("/api/users")
public class UserController {

    @GetMapping("/{id}")
    public ResponseEntity<UserDTO> getUser(@PathVariable Long id) {
        UserDTO user = userService.findById(id);
        return ResponseEntity.ok(user);
    }
}

// 其他服务调用(RestTemplate / WebClient)
UserDTO user = restTemplate.getForObject(
    "http://user-service/api/users/{id}", UserDTO.class, userId);

gRPC 示例

protobuf
// user.proto
service UserService {
    rpc GetUser(GetUserRequest) returns (UserResponse);
    rpc ListUsers(ListUsersRequest) returns (stream UserResponse); // 服务端流
}

message GetUserRequest {
    int64 id = 1;
}

message UserResponse {
    int64 id = 1;
    string name = 2;
    string email = 3;
}
java
// gRPC 客户端调用
UserResponse user = userServiceStub.getUser(
    GetUserRequest.newBuilder().setId(userId).build()
);

4. 服务发现

微服务实例动态变化(扩缩容、故障重启),调用方需要知道目标服务的实际地址。

客户端发现 (Client-side Discovery)

                    ┌──────────────┐
                    │ 服务注册中心    │
                    │ (Registry)    │
                    └──────┬───────┘
                      ▲    │
           1.注册     │    │ 2.查询服务实例列表
                      │    ▼
┌──────────┐      ┌──────────┐
│ 服务实例   │      │  调用方    │
│ A-1, A-2  │◄─────│ (Client)  │  3. 客户端负载均衡,直接调用
└──────────┘      └──────────┘

代表:Eureka + Ribbon、Nacos + Spring Cloud LoadBalancer

服务端发现 (Server-side Discovery)

                    ┌──────────────┐
                    │ 服务注册中心    │
                    │ (Registry)    │
                    └──────┬───────┘
                           │ 2.查询实例

┌──────────┐      ┌──────────────┐      ┌──────────┐
│  调用方    │─────►│ 负载均衡器     │─────►│ 服务实例   │
│ (Client)  │      │ (LB/Gateway) │      │ A-1, A-2  │
└──────────┘      └──────────────┘      └──────────┘
       1.请求           3.转发请求

代表:Kubernetes ServiceConsul + Envoy

主流注册中心对比

特性EurekaNacosConsul
一致性协议AP(最终一致)CP/AP 可切换CP(Raft)
健康检查客户端心跳客户端心跳 + 服务端探测多种(HTTP/TCP/gRPC/脚本)
配置管理不支持支持(内置配置中心)支持(KV 存储)
生态Spring Cloud NetflixSpring Cloud Alibaba跨语言,K8s 友好
维护状态Netflix 停止维护阿里巴巴活跃维护HashiCorp 活跃维护

5. API 网关

API 网关是微服务架构的统一入口,承担请求路由、身份认证、限流熔断等横切关注点。

                         ┌─────────────────┐
                         │    API Gateway    │
                         │                   │
  客户端 ──── HTTPS ────►│  路由 (Routing)    │──► 用户服务
                         │  认证 (Auth)       │──► 订单服务
                         │  限流 (Rate Limit) │──► 支付服务
                         │  日志 (Logging)    │──► 商品服务
                         │  监控 (Metrics)    │
                         └─────────────────┘
功能说明
路由转发根据 URL 路径将请求路由到对应的后端服务
身份认证统一鉴权(JWT 验签、OAuth 2.0),后端服务无需重复鉴权
限流熔断在网关层对请求进行限流和熔断,保护后端服务
协议转换对外 REST,对内 gRPC;或 HTTP → WebSocket
请求聚合将多个后端服务的响应聚合为一个响应返回给客户端
灰度发布按用户、流量比例路由到不同版本的服务

BFF 模式 (Backend For Frontend)

针对不同客户端(Web、App、小程序)的差异化需求,为每种客户端提供专属的 API 层:

┌───────┐  ┌───────┐  ┌───────┐
│  Web  │  │  App  │  │ 小程序 │
└───┬───┘  └───┬───┘  └───┬───┘
    │          │          │
    ▼          ▼          ▼
┌───────┐  ┌───────┐  ┌───────┐
│BFF-Web│  │BFF-App│  │BFF-Mini│   ← 各 BFF 按客户端需求裁剪数据
└───┬───┘  └───┬───┘  └───┬───┘
    │          │          │
    └──────────┼──────────┘

         微服务集群

优势: 各端的接口格式和数据粒度可以独立优化,避免通用 API 的"一刀切"问题。

6. 分布式事务

微服务架构中,一个业务操作可能跨多个服务和数据库,传统的单机事务(ACID)无法直接适用。

2PC(两阶段提交)

                 ┌──────────────┐
                 │ 事务协调者 (TC) │
                 └──────┬───────┘

         ┌──────────────┼──────────────┐
         ▼              ▼              ▼
    ┌─────────┐   ┌─────────┐   ┌─────────┐
    │ 参与者 A  │   │ 参与者 B  │   │ 参与者 C  │
    └─────────┘   └─────────┘   └─────────┘

阶段一 (Prepare):TC → 所有参与者: "准备好了吗?"  参与者执行事务但不提交
阶段二 (Commit/Rollback):全部 YES → Commit;任一 NO → Rollback

缺点: 同步阻塞、单点故障(协调者宕机)、数据不一致风险。

TCC(Try-Confirm-Cancel)

        Try(预留资源)     Confirm(确认提交)    Cancel(回滚释放)
账户 A:  冻结 100 元    →    扣减 100 元       /   解冻 100 元
账户 B:  预增 100 元    →    确认增加 100 元    /   取消预增
java
// TCC 接口定义
public interface AccountTccService {
    // Try: 冻结金额
    @TwoPhaseBusinessAction(name = "deduct",
        commitMethod = "confirm", rollbackMethod = "cancel")
    boolean tryDeduct(BusinessActionContext ctx,
                      @BusinessActionContextParameter(paramName = "amount") BigDecimal amount);

    // Confirm: 扣减冻结金额
    boolean confirm(BusinessActionContext ctx);

    // Cancel: 解冻金额
    boolean cancel(BusinessActionContext ctx);
}

优点: 性能好(无全局锁),灵活性高。缺点: 业务侵入性强,每个服务需要实现三个接口。

Saga 模式

正向流程:  T1(创建订单) → T2(扣减库存) → T3(扣减余额) → 成功
补偿流程(T3 失败时):C2(恢复库存) → C1(取消订单)
  • 编排式(Choreography): 每个服务监听事件并触发下一步,无中心协调者
  • 协调式(Orchestration): 由一个 Saga 协调者统一编排事务流程

分布式事务方案对比

方案一致性性能业务侵入性适用场景
2PC强一致低(同步阻塞)数据库层面的分布式事务
TCC最终一致高(需实现三个接口)金融、支付等对一致性要求高的场景
Saga最终一致中(需实现补偿操作)长事务、跨多个服务的业务流程
本地消息表最终一致异步可靠消息,如通知、积分

7. 可观测性三大支柱

微服务将单体拆散成几十甚至上百个服务后,"程序出问题了,但不知道问题在哪个服务"是最常见的运维痛点。可观测性(Observability)通过三大支柱解决这个问题。

可观测性三大支柱

┌──────────────┐  ┌──────────────┐  ┌──────────────┐
│  分布式追踪    │  │     指标      │  │     日志      │
│  (Tracing)   │  │  (Metrics)   │  │  (Logging)   │
│              │  │              │  │              │
│ "请求经过了    │  │ "系统现在运行  │  │ "出错时发生了 │
│  哪些服务?"   │  │  得怎么样?"   │  │  什么事?"    │
│              │  │              │  │              │
│ Jaeger       │  │ Prometheus   │  │ Elasticsearch│
│ Zipkin       │  │ Grafana      │  │ Logstash     │
│ SkyWalking   │  │ RED/USE 方法  │  │ Kibana (ELK) │
└──────────────┘  └──────────────┘  └──────────────┘
         统一标准:OpenTelemetry(OTEL)

分布式追踪(Distributed Tracing)

核心概念:

  • TraceID: 一次完整请求的唯一标识,贯穿所有服务
  • SpanID: 单个服务处理片段的标识;多个 Span 组成一棵调用树(Trace)
  • OpenTelemetry(OTEL): CNCF 主导的追踪标准,统一 API / SDK / 数据格式,避免厂商锁定
用户请求(TraceID: abc123)

├── [Span 1] API 网关           0ms - 120ms
│   ├── [Span 2] 用户服务        5ms -  20ms
│   └── [Span 3] 订单服务       25ms - 110ms
│       ├── [Span 4] 库存服务   30ms -  60ms
│       └── [Span 5] 支付服务   65ms - 105ms  ← 瓶颈在这里

主流工具对比:

工具特点适用场景
JaegerCNCF 毕业项目,原生 OTEL 支持,部署轻量云原生、K8s 环境
ZipkinTwitter 开源,社区成熟,资源占用小中小规模,快速上手
SkyWalkingApache 项目,Java 生态友好,支持多语言 Agent国内 Java 微服务团队

指标监控(Metrics)

Prometheus + Grafana 是事实上的标准组合:

微服务实例 → Prometheus(拉取/推送指标)→ 存储时序数据
                                        → Grafana(可视化仪表盘)
                                        → AlertManager(告警)

RED 方法(针对面向请求的服务):

指标含义示例
Rate(速率)每秒处理请求数(QPS/RPS)http_requests_total
Errors(错误率)失败请求占比http_requests_total{status="5xx"}
Duration(延迟)请求响应时间(P50/P95/P99)http_request_duration_seconds

USE 方法(针对基础设施资源):

指标含义
Utilization(使用率)CPU 使用率、内存占用
Saturation(饱和度)队列积压、线程池等待数
Errors(错误数)磁盘 I/O 错误、网络丢包

关键业务指标:

  • 接口 P99 延迟(慢请求感知)
  • 服务错误率(5xx 比例)
  • 数据库连接池使用率
  • 消息队列积压数(消费延迟)
  • JVM GC 停顿时间(Java 服务)

日志(Logging)

ELK / EFK 栈:

微服务日志输出


Filebeat / Fluentd(采集)


Logstash(过滤、解析、转换)


Elasticsearch(存储、索引)


Kibana(查询、可视化)

结构化日志(Structured Logging)最佳实践:

java
// 不推荐:纯文本日志,难以检索
logger.info("用户 123 下单失败,原因:库存不足");

// 推荐:结构化 JSON 日志,包含 TraceID 关联
logger.info("order.create.failed",
    Map.of(
        "traceId", MDC.get("traceId"),   // 与 Span 关联
        "userId", 123,
        "reason", "INSUFFICIENT_STOCK",
        "productId", 456
    )
);
// 输出: {"timestamp":"...","level":"INFO","event":"order.create.failed",
//        "traceId":"abc123","userId":123,"reason":"INSUFFICIENT_STOCK","productId":456}

关键实践:

  • 日志中必须包含 TraceID / SpanID,实现日志与追踪的关联(Correlation)
  • 使用结构化 JSON 格式,便于 Elasticsearch 建索引和查询
  • 日志级别规范:ERROR(需告警)/ WARN(潜在问题)/ INFO(关键业务事件)/ DEBUG(本地调试)

8. 容器与编排

微服务的价值只有在能够快速、可靠地部署时才能真正发挥出来,容器化和 Kubernetes 编排是实现这一目标的基础设施。

Docker 基础

概念说明
镜像(Image)只读的应用打包模板,包含代码、运行时、依赖(通过 Dockerfile 构建)
容器(Container)镜像运行时的实例,相互隔离,可启停
网络模式bridge(默认,容器间互通)/ host(共享宿主机网络)/ none(无网络)/ overlay(跨主机)
dockerfile
# 典型 Java 微服务 Dockerfile
FROM eclipse-temurin:17-jre-alpine
WORKDIR /app
COPY target/user-service.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

Kubernetes 核心概念

K8s 集群结构

┌────────────────────────────────────────────────────┐
│                    Namespace                         │
│                                                      │
│  ┌──────────────────────┐  ┌──────────────────────┐  │
│  │     Deployment        │  │       Service         │  │
│  │  ┌────────┐           │  │                      │  │
│  │  │  Pod   │ ReplicaSet│  │  ClusterIP / NodePort│  │
│  │  │ [容器]  │           │  │  / LoadBalancer      │  │
│  │  └────────┘           │  │                      │  │
│  └──────────────────────┘  └──────────────────────┘  │
│                                                      │
│  ┌──────────────────────┐                            │
│  │       Ingress         │ ← 集群外部流量入口          │
│  │  路由规则(域名/路径)  │                            │
│  └──────────────────────┘                            │
└────────────────────────────────────────────────────┘
资源作用
PodK8s 最小调度单元,包含一个或多个容器,共享网络和存储
Deployment管理 Pod 副本数、滚动升级、回滚
Service为一组 Pod 提供稳定的虚拟 IP 和 DNS 名称(负载均衡)
Ingress集群外部流量入口,基于域名/路径路由到不同 Service
ConfigMap / Secret外部化配置与敏感信息
HPAHorizontal Pod Autoscaler,根据 CPU/内存/自定义指标自动扩缩容

Pod 生命周期与健康检查

Pod 生命周期:
Pending → Running → Succeeded / Failed / Unknown

三种探针:

┌────────────────┬──────────────────────────────────────┐
│ Liveness Probe │ 检测容器是否存活;失败则重启容器           │
│                │ 用于:死锁、OOM 等导致服务无响应的场景      │
├────────────────┼──────────────────────────────────────┤
│ Readiness Probe│ 检测容器是否就绪接收流量;失败则从 Service  │
│                │ 的 Endpoints 中移除,不再转发请求          │
├────────────────┼──────────────────────────────────────┤
│ Startup Probe  │ 容器启动阶段专用;避免慢启动应用被            │
│                │ Liveness Probe 提前杀掉                  │
└────────────────┴──────────────────────────────────────┘
yaml
# 健康检查示例
livenessProbe:
  httpGet:
    path: /actuator/health/liveness
    port: 8080
  initialDelaySeconds: 30
  periodSeconds: 10
  failureThreshold: 3

readinessProbe:
  httpGet:
    path: /actuator/health/readiness
    port: 8080
  initialDelaySeconds: 10
  periodSeconds: 5

9. Service Mesh(服务网格)

Service Mesh 将服务间通信的治理逻辑从业务代码中剥离,下沉到基础设施层(Sidecar 代理)。

┌──────────────────────────────────────────────┐
│                   Pod A                        │
│  ┌──────────┐    ┌─────────────────────────┐  │
│  │ 业务服务 A │────│ Sidecar Proxy (Envoy)    │  │
│  └──────────┘    └────────────┬────────────┘  │
└───────────────────────────────┼───────────────┘
                                │ mTLS / 负载均衡 / 限流

┌───────────────────────────────┼───────────────┐
│                   Pod B       │                │
│  ┌──────────┐    ┌────────────┴────────────┐  │
│  │ 业务服务 B │────│ Sidecar Proxy (Envoy)    │  │
│  └──────────┘    └─────────────────────────┘  │
└──────────────────────────────────────────────┘

控制平面 (Istio Control Plane):
  ┌────────┐  ┌────────┐  ┌────────┐
  │ Istiod  │  │ 配置下发 │  │ 证书管理 │
  └────────┘  └────────┘  └────────┘

核心能力:

  • 流量管理: 负载均衡、流量分割(灰度发布)、故障注入
  • 安全: mTLS 加密通信、访问控制策略
  • 可观测性: 分布式追踪、指标采集、日志采集

优势: 业务代码零侵入,通信治理逻辑统一管理。 代价: 增加运维复杂度和资源开销(每个 Pod 多一个 Sidecar 容器)。

10. CAP 定理

分布式系统中,以下三个属性最多只能同时满足两个:

属性说明
C(Consistency,一致性)所有节点在同一时刻看到相同的数据
A(Availability,可用性)每个请求都能在合理时间内获得非错误的响应
P(Partition Tolerance,分区容错)网络分区(节点间通信中断)时系统仍能继续工作

在分布式系统中,网络分区(P)是不可避免的,因此实际选择是 CP 还是 AP

类型说明代表系统
CP网络分区时保证一致性,可能拒绝服务ZooKeeper、etcd、HBase、Redis Cluster
AP网络分区时保证可用性,数据可能暂时不一致Eureka、Cassandra、DynamoDB、DNS

实际工程中的权衡:

  • 金融场景(转账、扣款)→ 倾向 CP,数据一致性优先
  • 电商场景(商品信息、用户画像)→ 倾向 AP,可用性优先,接受最终一致
  • 大多数系统采用 BASE 理论(基本可用 + 软状态 + 最终一致),在 CAP 之间做平衡

技术选型与对比

单体 vs 微服务

维度单体架构微服务架构
部署方式整体打包部署各服务独立部署
技术栈统一可异构(Java / Go / Python 混用)
扩展性整体扩容按需对单个服务扩容
故障隔离一个模块故障影响全局单个服务故障不影响其他服务
开发效率初期快,后期急剧下降初期慢(基础设施建设),后期高效
运维复杂度高(需要容器化、编排、监控等基础设施)
数据管理单一数据库每个服务独立数据库(Database per Service)
适用团队小团队(< 10 人)中大型团队(多个独立小组)

微服务不是银弹。小型项目盲目引入微服务,会引入过多的分布式系统复杂度,得不偿失。

服务网格选型

工具特点适用场景
Istio + Envoy功能最全,社区最活跃大规模 K8s 生产环境
Linkerd轻量,资源占用低,Rust 实现资源敏感场景
Consul Connect与 Consul 服务发现深度集成已用 Consul 的团队

实战案例:单体迁移微服务

以一个电商单体系统为例,演示分阶段迁移路径。

阶段一:识别限界上下文

分析单体内部模块依赖:

用户模块 ──────────── 订单模块
   │                    │
   │                    ├── 库存模块
   │                    │
   └── 支付模块 ─────────┘

识别高内聚、低耦合的业务边界 → 确定拆分优先级:
优先拆分:库存服务(独立扩容需求 + 边界清晰)

阶段二:提取第一个服务

步骤:
1. 新建独立代码仓库 inventory-service
2. 单体中的库存模块代码迁移到新服务
3. 新服务有自己的数据库(inventory_db)
4. 单体通过 HTTP 调用新服务,不再直接访问原库存表
5. 双写 + 数据迁移,灰度切流验证

单体(仍运行)

   ├── 用户模块
   ├── 订单模块 ──── HTTP ────► 库存服务(新)
   └── 支付模块

阶段三:引入 API 网关与服务发现

外部流量 → API 网关(Kong / Spring Cloud Gateway)

              ├──► 库存服务(注册到 Nacos)
              └──► 单体(仍处理其他请求)

单体内部服务调用开始通过注册中心发现服务地址

阶段四:逐步提取剩余服务

优先级排序(按业务价值 + 拆分难度):
1. 库存服务 ✓ 已完成
2. 用户服务 → 拆分(边界清晰,无复杂依赖)
3. 支付服务 → 拆分(边界清晰,但需要严格的分布式事务)
4. 订单服务 → 最后拆分(依赖最多,最复杂)

最终形态:
API 网关 → [用户服务][订单服务][库存服务][支付服务]
           各服务独立数据库,通过消息队列进行异步解耦

关键经验:

  • 每次只拆一个服务,验证稳定后再拆下一个
  • 数据迁移期间保持双写,用对比监控发现不一致
  • 不要大爆炸式重写,绞杀者模式是最低风险的迁移方式

面试常问 & 怎么答

Q1: 分布式事务方案对比

2PC(两阶段提交):

  • 阶段一(Prepare):协调者询问所有参与者是否可以提交,参与者执行事务但不提交
  • 阶段二(Commit):全部同意则提交,任一拒绝则回滚
  • 缺点:同步阻塞,性能差;协调者单点故障;阶段二可能出现部分提交导致数据不一致

TCC(Try-Confirm-Cancel):

  • Try:预留资源(如冻结余额);Confirm:确认提交;Cancel:回滚释放
  • 优点:性能好,适合金融场景。缺点:业务侵入性强,每个服务需实现三个接口

Saga:

  • 将长事务拆为多个本地事务,每个本地事务有对应的补偿操作
  • 适合跨多个服务的长流程,业务侵入性适中

面试回答要点: 大多数场景不需要强一致,优先选择 Saga(通用性好)或 TCC(金融场景),避免使用 2PC(性能差且有单点故障风险)。

Q2: CAP 定理与 CP/AP 区别

CAP 定理指出: 分布式系统不可能同时满足一致性(C)、可用性(A)、分区容错性(P)。由于网络分区不可避免,实际选择是 CP 还是 AP。

CP 系统示例 -- ZooKeeper:

  • 当 Leader 节点与 Follower 发生网络分区时,少数派节点拒绝提供服务(牺牲可用性)
  • 适用于:分布式锁、配置中心、Leader 选举

AP 系统示例 -- Eureka:

  • 当节点间网络分区时,每个节点仍然接受注册和查询(保证可用性)
  • 各节点间的数据可能暂时不一致(牺牲一致性)

实际工程中 大多数系统采用 BASE 理论(基本可用 + 软状态 + 最终一致),在一致性和可用性之间做程度折中。

Q3: 微服务通信方式选择

方式适用场景不适用场景
REST对外 API、前后端通信、跨组织集成内部高频调用(性能不够)
gRPC内部服务间高频同步调用(性能高 5-10x)需要浏览器直接访问的 API
消息队列异步处理、事件驱动、削峰填谷需要实时响应的场景

回答要点: 实际项目通常混合使用——对外 REST、内部 gRPC、异步事件用消息队列。

Q4: 微服务的优缺点?什么时候不应该用微服务?

优点:

  • 独立部署:各服务可以独立上线,发布周期互不阻塞
  • 独立扩容:高负载服务单独扩容,成本可控
  • 技术异构:各团队可以选择最适合的语言和框架
  • 故障隔离:单个服务崩溃不会拖垮整个系统

缺点:

  • 分布式系统复杂度:网络延迟、部分失败、数据一致性等问题无法回避
  • 运维成本高:需要容器化、编排平台、服务发现、分布式追踪等完整基础设施
  • 开发复杂度上升:本地联调需要启多个服务,分布式事务比单机事务难一个量级

什么时候不应该用微服务:

  1. 团队规模小(< 10 人): 构建和维护微服务基础设施的成本大于收益,单体更合适
  2. 业务边界不清晰: 强行拆分导致频繁跨服务调用和分布式事务,得不偿失
  3. 产品早期(PMF 前): 需求变化频繁,微服务的服务边界会不断重划,维护成本极高
  4. 团队缺乏分布式系统经验: 没有做好基础设施建设就引入微服务,会导致线上问题频发

面试回答模板: "微服务解决的是规模化的组织和技术问题,前提是团队规模足够大、业务边界足够清晰、基础设施足够成熟。对于小团队或早期产品,单体架构加合理的模块化设计往往是更好的选择。"

Q5: 如何做服务拆分?拆分粒度怎么把握?

拆分方法:

  1. 以 DDD 限界上下文为边界: 找到业务中天然存在的"语言边界"——同一个词在不同上下文有不同含义,就说明存在边界。例如"订单"在订单域和支付域的含义和字段不同。

  2. 观察团队协作摩擦: 频繁等待其他团队合并代码、频繁协调发布计划,说明应该在这里划分边界,让各团队独立拥有代码库。

  3. 识别差异化扩容需求: 搜索服务、推荐服务计算密集,需要单独扩容;用户服务 QPS 稳定,不需要频繁扩缩容——这是拆分的信号。

粒度把握原则:

  • 不要按技术层拆分(Controller 服务 / Service 服务 / DAO 服务 = 错误)
  • 服务应围绕业务能力,而非功能(用户服务、订单服务,而非读服务、写服务)
  • 拆分后,跨服务调用应远少于服务内调用;如果拆后跨服务调用反而增多,说明拆错了
  • 两个团队 = 两个服务的上限;一个团队维护太多服务也会失控

面试回答模板: "拆分没有绝对标准,核心是'高内聚、低耦合'。我会从 DDD 限界上下文入手识别业务边界,结合团队结构(Conway's Law:系统架构趋向于组织架构)、独立发布需求和扩容差异来决定拆分边界。粒度的判断标准是:拆分后,服务内调用远大于跨服务调用;每个服务有独立的业务语义,可以用一句话描述它的职责。"

看到什么就先想到这类

看到 / 问到第一反应
单体应用变慢 / 部署困难微服务拆分 → DDD 限界上下文识别边界 → 绞杀者模式迁移
服务间调用追踪 / 慢请求定位分布式追踪(Jaeger / Zipkin / SkyWalking)+ TraceID 串联
K8s 部署 / 扩缩容 / 健康检查Pod / Deployment / Service / HPA + Liveness & Readiness Probe
零侵入服务治理 / 灰度发布Service Mesh(Istio + Envoy Sidecar)
多端差异化 API(Web / App / 小程序)BFF 模式(每个客户端一个专属 API 层)
系统出问题但不知在哪个服务可观测性三大支柱:Tracing + Metrics(RED/USE)+ Logging(ELK)
跨服务的业务操作需要事务优先 Saga(通用)或 TCC(金融),避免 2PC
服务实例动态扩缩容 / 需要服务地址服务发现(Nacos / Consul / K8s Service)
什么时候不应该用微服务团队小、业务边界不清、产品早期、缺乏分布式基础设施

常见误区

易错点

  1. 微服务不是越小越好: 拆分粒度过细会导致服务间调用频繁、分布式事务复杂、运维成本高。应按业务领域(DDD 限界上下文)合理拆分
  2. 分布式事务不一定需要强一致: 很多场景只需最终一致性。优先考虑 Saga + 补偿 或 本地消息表,避免使用 2PC
  3. 服务通信不要只用 REST: 内部服务间高频调用更适合 gRPC(性能高 5-10 倍),异步场景适合消息队列
  4. CAP 不是三选二: 网络分区(P)在分布式系统中不可避免,实际选择是 CP 还是 AP。而且 CAP 的 C/A 定义非常严格,实际系统通常在两者之间做程度折中
  5. API 网关不要承担业务逻辑: 网关只做路由、鉴权、限流等横切关注点,业务逻辑应放在后端服务中

延伸阅读