第 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 章:消息队列 — 深入消息队列、事件驱动架构与最终一致性。