微服务拆分精讲 / 第 07 章:服务通信
第 07 章:服务通信
服务间的通信方式决定了系统的耦合度、性能和可靠性。选对通信模式,是微服务成功的关键。
7.1 通信模式总览
7.1.1 同步 vs 异步
同步通信 (Synchronous)
──────────────────────────────
服务A ──请求──▶ 服务B
服务A ◀──响应── 服务B
特点:调用方等待响应,阻塞直到完成
异步通信 (Asynchronous)
──────────────────────────────
服务A ──消息──▶ [消息队列] ──消费──▶ 服务B
特点:调用方发送消息后立即返回,不等待响应
7.1.2 通信模式矩阵
| 模式 |
参与方数量 |
通信方式 |
示例 |
| 请求-响应 |
1:1 |
同步 |
REST API, gRPC |
| 单向通知 |
1:1 |
异步 |
事件通知 |
| 发布-订阅 |
1:N |
异步 |
广播事件 |
| 请求-异步响应 |
1:1 |
异步 |
回调模式 |
请求-响应 发布-订阅
───────── ─────────
A ──请求──▶ B A ──事件──▶ Topic
A ◀──响应── B ├──▶ B
├──▶ C
└──▶ D
7.2 同步通信:REST
7.2.1 REST API 设计规范
| 规范 |
说明 |
示例 |
| 资源命名 |
使用名词复数 |
/api/v1/users |
| HTTP 方法 |
GET/POST/PUT/DELETE 语义 |
GET /users/{id} |
| 状态码 |
正确使用 HTTP 状态码 |
200/201/400/404/500 |
| 版本化 |
URL 或 Header 中体现版本 |
/api/v1/ |
| 分页 |
列表接口支持分页 |
?page=1&size=20 |
REST API 设计示例:
┌────────┬──────────────────────┬───────────────┐
│ 方法 │ 路径 │ 说明 │
├────────┼──────────────────────┼───────────────┤
│ GET │ /api/v1/users │ 获取用户列表 │
│ GET │ /api/v1/users/{id} │ 获取单个用户 │
│ POST │ /api/v1/users │ 创建用户 │
│ PUT │ /api/v1/users/{id} │ 更新用户 │
│ DELETE │ /api/v1/users/{id} │ 删除用户 │
│ GET │ /api/v1/users/{id}/orders │ 用户的订单 │
└────────┴──────────────────────┴───────────────┘
7.2.2 REST 的优缺点
| 维度 |
优势 |
劣势 |
| 易用性 |
HTTP 通用,易理解 |
冗余头部开销 |
| 调试 |
浏览器/Postman 可直接调试 |
— |
| 性能 |
— |
JSON 序列化开销大 |
| 类型安全 |
— |
缺乏强类型约束 |
| 代码生成 |
— |
需要 OpenAPI/Swagger |
| 流式传输 |
— |
不支持流式通信 |
7.2.3 OpenAPI/Swagger 规范
# openapi.yaml
openapi: 3.0.0
info:
title: User Service API
version: 1.0.0
paths:
/api/v1/users/{id}:
get:
summary: 获取用户详情
parameters:
- name: id
in: path
required: true
schema:
type: string
responses:
'200':
description: 成功
content:
application/json:
schema:
$ref: '#/components/schemas/User'
'404':
description: 用户不存在
components:
schemas:
User:
type: object
properties:
id:
type: string
name:
type: string
email:
type: string
format: email
7.3 同步通信:gRPC
7.3.1 gRPC 概述
gRPC 是 Google 开发的高性能 RPC 框架,基于 HTTP/2 和 Protocol Buffers。
gRPC 通信流程:
┌──────────┐ ┌──────────┐
│ 客户端 │ │ 服务端 │
│ │ HTTP/2 连接 │ │
│ Proto │ ◀══════════════════════▶ │ Proto │
│ Client │ 二进制传输 (Protobuf) │ Server │
│ │ │ │
│ .proto │ ┌──────────────────┐ │ .proto │
│ 文件生成 │ │ IDL 接口定义 │ │ 文件生成 │
│ 客户端桩 │ │ service UserService│ │ 服务端骨架│
└──────────┘ │ { │ └──────────┘
│ rpc GetUser │
│ (GetUserReq) │
│ returns │
│ (UserResp) │
│ } │
└──────────────────┘
7.3.2 Protocol Buffers 定义
// user.proto
syntax = "proto3";
package user;
option java_package = "com.example.user";
service UserService {
rpc GetUser (GetUserRequest) returns (UserResponse);
rpc ListUsers (ListUsersRequest) returns (ListUsersResponse);
rpc CreateUser (CreateUserRequest) returns (UserResponse);
rpc WatchUser (WatchUserRequest) returns (stream UserEvent); // 服务端流
}
message GetUserRequest {
string id = 1;
}
message UserResponse {
string id = 1;
string name = 2;
string email = 3;
int64 created_at = 4;
}
message ListUsersRequest {
int32 page = 1;
int32 size = 2;
}
message ListUsersResponse {
repeated UserResponse users = 1;
int32 total = 2;
}
message CreateUserRequest {
string name = 1;
string email = 2;
}
message WatchUserRequest {
string user_id = 1;
}
message UserEvent {
string event_type = 1; // CREATED, UPDATED, DELETED
UserResponse user = 2;
int64 timestamp = 3;
}
7.3.3 gRPC 四种通信模式
1. 一元 RPC (Unary)
客户端 ──请求──▶ 服务端
客户端 ◀──响应── 服务端
2. 服务端流式 (Server Streaming)
客户端 ──请求──▶ 服务端
客户端 ◀──stream── 服务端
客户端 ◀──stream── 服务端
客户端 ◀──stream── 服务端
3. 客户端流式 (Client Streaming)
客户端 ──stream──▶ 服务端
客户端 ──stream──▶ 服务端
客户端 ──stream──▶ 服务端
客户端 ◀──响应── 服务端
4. 双向流式 (Bidirectional Streaming)
客户端 ──stream──▶ 服务端
客户端 ◀──stream── 服务端
客户端 ──stream──▶ 服务端
客户端 ◀──stream── 服务端
7.3.4 REST vs gRPC 全面对比
| 维度 |
REST |
gRPC |
| 协议 |
HTTP/1.1 |
HTTP/2 |
| 数据格式 |
JSON (文本) |
Protobuf (二进制) |
| 性能 |
较低 |
极高(2-10 倍) |
| 类型安全 |
弱 (依赖文档) |
强 (IDL 编译时检查) |
| 代码生成 |
OpenAPI (可选) |
必须 (protoc) |
| 浏览器支持 |
✅ 原生支持 |
⚠️ 需要 gRPC-Web |
| 流式传输 |
❌ 不支持 |
✅ 四种模式 |
| 调试 |
✅ curl/浏览器 |
⚠️ 需要专用工具 |
| 学习曲线 |
低 |
中 |
| 适用场景 |
外部 API |
服务间高性能通信 |
7.4 通信模式选型
7.4.1 选型决策树
需要同步响应吗?
│
├── 是 → 需要高性能/低延迟?
│ │
│ ├── 是 → 使用 gRPC
│ │ (服务间内部通信)
│ │
│ └── 否 → 使用 REST
│ (外部 API / 简单场景)
│
└── 否 → 需要保证消息不丢失?
│
├── 是 → 使用消息队列
│ (Kafka / RabbitMQ)
│
└── 否 → 使用事件通知
(轻量级异步)
7.4.2 通信方式适用场景
| 场景 |
推荐方式 |
理由 |
| 外部客户端 API |
REST |
兼容性好,易调试 |
| 服务间内部调用(简单) |
REST |
实现简单,团队熟悉 |
| 服务间内部调用(高性能) |
gRPC |
低延迟,强类型 |
| 实时推送/流式 |
gRPC Streaming |
原生流式支持 |
| 事件通知 |
消息队列 |
解耦、可靠 |
| 批量数据同步 |
消息队列 |
削峰填谷 |
| 实时聊天/协作 |
WebSocket / gRPC 双向流 |
双向实时通信 |
7.5 服务发现(Service Discovery)
7.5.1 服务发现方式
客户端发现 服务端发现
──────────── ────────────
┌────────┐ ┌────────┐
│ 客户端 │ │ 客户端 │
└───┬────┘ └───┬────┘
│ │
▼ ▼
┌──────────┐ ┌──────────┐
│ 服务注册 │ │ 负载均衡 │
│ 中心 │ │ / 代理 │
│(Eureka/ │ │(LB/Nginx)│
│ Consul) │ └────┬─────┘
└──────────┘ │
│ ▼
▼ ┌──────────┐
┌──────────┐ │ 服务实例 │
│ 服务实例 │ └──────────┘
└──────────┘
7.5.2 注册中心对比
| 注册中心 |
一致性协议 |
健康检查 |
配置管理 |
适用场景 |
| Consul |
Raft |
HTTP/TCP/gRPC |
KV Store |
通用,多数据中心 |
| Etcd |
Raft |
TTL/Lease |
KV Store |
K8s 生态 |
| Nacos |
AP/CP 可选 |
HTTP/TCP |
配置中心 |
Spring Cloud Alibaba |
| ZooKeeper |
ZAB |
会话 |
节点 |
传统 Java 项目 |
| Eureka |
AP |
心跳 |
— |
Netflix 生态(已维护模式) |
7.6 弹性通信模式
7.6.1 超时、重试与熔断
┌──────────────────────────────────────────────────────────┐
│ 弹性通信三板斧 │
├──────────────────────────────────────────────────────────┤
│ │
│ 1. 超时 (Timeout) │
│ ┌────────┐ ──请求──▶ ┌────────┐ │
│ │ 服务A │ 3秒超时 │ 服务B │ │
│ │ │ ◀─超时!── │ (卡住) │ │
│ └────────┘ └────────┘ │
│ │
│ 2. 重试 (Retry) │
│ ┌────────┐ ──请求1──▶ ┌────────┐ │
│ │ 服务A │ ◀─失败── │ 服务B │ │
│ │ │ ──请求2──▶ │ │ │
│ │ │ ◀─成功── │ │ │
│ └────────┘ └────────┘ │
│ │
│ 3. 熔断 (Circuit Breaker) │
│ ┌────────┐ ┌────────┐ │
│ │ 服务A │ ──请求──▶ │ 服务B │ (连续失败) │
│ │ │ └────────┘ │
│ │ 熔断器 │ ──直接──▶ 返回降级响应 │
│ │ CLOSED │ (不再调用B) │
│ │ ↓ │ │
│ │ OPEN │ 一段时间后 ──半开──▶ 探测请求 │
│ └────────┘ 恢复? CLOSED : OPEN │
└──────────────────────────────────────────────────────────┘
7.6.2 熔断器状态机
┌─────────────┐ 失败率超过阈值 ┌─────────────┐
│ CLOSED │ ──────────────────▶ │ OPEN │
│ (正常状态) │ │ (熔断状态) │
│ 所有请求 │ │ 所有请求 │
│ 通过 │ │ 直接拒绝 │
└──────┬──────┘ └──────┬──────┘
│ │
│ 正常响应 │ 等待超时后
│ ▼
│ ┌──────────────┐
│◀────────────────────────── │ HALF-OPEN │
│ 探测成功,恢复 │ (半开状态) │
│ │ 允许少量请求 │
│ └──────────────┘
│ │
│ 探测失败│
│ ▼
│ 回到 OPEN 状态
7.6.3 Resilience4j 示例
// 熔断器配置
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
.failureRateThreshold(50) // 失败率阈值 50%
.waitDurationInOpenState(Duration.ofSeconds(30)) // 熔断等待 30 秒
.slidingWindowSize(10) // 滑动窗口 10 次请求
.minimumNumberOfCalls(5) // 最少 5 次调用才计算
.build();
CircuitBreaker circuitBreaker = CircuitBreaker.of("userService", config);
// 使用
Supplier<User> decoratedSupplier = CircuitBreaker
.decorateSupplier(circuitBreaker, () -> userService.getUser(userId));
Try<User> result = Try.ofSupplier(decoratedSupplier)
.recover(CallNotPermittedException.class, e -> getFallbackUser())
.recover(Exception.class, e -> getDefaultUser());
7.7 业务场景:电商系统的通信设计
┌───────────────────────────────────────────────────────────┐
│ 电商系统通信模式选型 │
├───────────────────────────────────────────────────────────┤
│ │
│ 外部 API(客户端 → 网关): │
│ └─ REST / HTTPS │
│ │
│ 服务间同步调用: │
│ ├─ 订单服务 → 用户服务 (查询用户): REST │
│ ├─ 订单服务 → 商品服务 (查询商品): gRPC │
│ └─ 订单服务 → 库存服务 (扣库存): gRPC (高性能) │
│ │
│ 服务间异步事件: │
│ ├─ 订单已创建 → Kafka → 支付服务/库存服务/通知服务 │
│ ├─ 支付已完成 → Kafka → 订单服务/物流服务 │
│ └─ 商品已发货 → Kafka → 通知服务/用户服务 │
│ │
│ 实时通信: │
│ └─ WebSocket / gRPC Streaming (订单状态实时推送) │
└───────────────────────────────────────────────────────────┘
⚠️ 注意事项
- 同步调用链不要太长——超过 3 层的同步调用应考虑异步化
- 设置合理的超时——每个调用都必须有超时,避免无限等待
- 重试要有退避策略——指数退避 + 抖动,避免重试风暴
- 熔断要配合降级——熔断后要有兜底响应
- gRPC 调试不如 REST 方便——确保有合适的调试工具
📖 扩展阅读
- gRPC Documentation (grpc.io) — gRPC 官方文档
- Resilience4j (resilience4j.readme.io) — Java 弹性通信库
- Sam Newman - Building Microservices, Chapter 4 — 同步通信
- Netflix Hystrix (已进入维护模式,推荐 Resilience4j 替代)
- Web API Design — API 设计最佳实践
本章小结
| 要点 |
说明 |
| 同步通信 |
REST(通用)和 gRPC(高性能)两种主要方式 |
| 异步通信 |
消息队列解耦服务,详见第 08 章 |
| 服务发现 |
Consul/Etcd/Nacos 是主流选择 |
| 弹性模式 |
超时 + 重试 + 熔断 = 稳定的服务通信 |
| 选型原则 |
外部用 REST,内部高性能用 gRPC,异步用 MQ |
📌 下一章:第 08 章:消息队列 — 深入消息队列、事件驱动架构与最终一致性。