全书结构
这一页是《Java 服务端通信工程》的总入口。它不只列标题,还给出三条可以照着走的路径、每章的验收信号,以及 linkd-v3 四个模块在什么阶段长出什么能力。
全书 50 篇,构成为:
| 部分 | 篇数 | 内容 |
|---|---|---|
| 前置 | 3 | 序 、前言 、目录 |
| 正文 | 45 | 15 章,每章 3 节 |
| 附录 | 2 | 通信 API 速查 、诊断与压测命令 |
正文十五章按能力分成五组:契约与调用(1–3)、传输与连接(4–6)、治理与入口(7–9)、安全与观测(10–12)、演练与收口(13–15)。分组不是按知识点分类,而是按「你现在能跑通什么」划分。每组的最后一章都留下一个可验证的能力,下一组在它上面继续。
linkd-v3 的模块状态
linkd-v3 是四模块工程,groupId com.plume.linkd.v3。能力随章节推进逐块长出来:
| 模块 | 职责 | 关键类型 | 首次出现 | 完成标志 |
|---|---|---|---|---|
comm-api | .proto、stub、错误码 | LibraryGrpc、Book、RpcError | 第 1.3 节搭骨架 | 9093 能编译出四个方法 |
comm-core | 发现、负载均衡、重试、熔断、限流、指标、追踪 | StaticRegistry、LibraryClient、RetryPolicy、TokenBucket | 第 3.3 节 | 客户端能带预算调用下游 |
comm-server | 服务实现、拦截器、健康检查、反射 | LibraryService、RequestContextInterceptor | 第 3.1 节 | 四个方法都能被调用 |
comm-gateway | HTTP/BFF、协议转换、下游调用 | BookHandler、ErrorMapper | 第 9.1 节 | 9094 能用 HTTP 访问 9093 |
模块之间单向依赖:comm-gateway、comm-server 依赖 comm-core,comm-core 依赖 comm-api。正文不会中途改模块划分;需要新类时,只往对应模块里加。
用一段代码确认依赖方向没有被写反:
package com.plume.linkd.v3.demo;
import java.util.List;
import java.util.Map;
public final class ModuleGraph {
private static final List<String> MODULES =
List.of("comm-api", "comm-core", "comm-server", "comm-gateway");
private static final Map<String, List<String>> DEPENDS_ON = Map.of(
"comm-api", List.of(),
"comm-core", List.of("comm-api"),
"comm-server", List.of("comm-core"),
"comm-gateway", List.of("comm-core"));
public static void main(String[] args) {
for (String module : MODULES) {
List<String> deps = DEPENDS_ON.get(module);
System.out.println(module + " -> " + (deps.isEmpty() ? "none" : String.join(",", deps)));
}
boolean apiDependsOnBusiness = DEPENDS_ON.get("comm-api").stream()
.anyMatch(dep -> dep.startsWith("comm-"));
System.out.println("direction.ok=" + !apiDependsOnBusiness);
}
}
预期输出:
comm-api -> none
comm-core -> comm-api
comm-server -> comm-core
comm-gateway -> comm-core
direction.ok=true
依赖方向一旦反过来,comm-api 就会拖进服务实现,客户端为了拿一个 .proto 生成类不得不引入整个服务端依赖。这种错误在单模块里看不出来,拆成多模块后才暴露。
三条学习路径
三条路径的入口相同,目标不同。选一条为主,其他章节按需补,不要三条同时推进。
路径一:把 RPC 用对(约 3 周)
适合第一次把 HTTP 调用换成 gRPC、或者要设计一套内部接口的人。主线是「契约先定,错误和时间先说清」。
| 周 | 章节 | 目标 |
|---|---|---|
| 1 | 第 1 章 | 能写出带错误码和兼容规则的 .proto |
| 2 | 第 2 章 | 能解释一个字段在线上的字节,知道哪些改动不兼容 |
| 3 | 第 3 章 + 4.1 Stub 与 Channel 生命周期 | 能设置 deadline、取消、幂等键和重试 |
验收信号:给出一个 GetBook 请求,能写出它的字节序列、失败时的状态码与原因码,以及“重试两次是否安全”的判断。
路径二:把治理做起来(约 5 周)
适合正在维护多服务调用、需要处理超时、重试、熔断和限流的人。主线是「故障在到达核心逻辑之前被吸收」。
| 周 | 章节 | 目标 |
|---|---|---|
| 1 | 3.2 Deadline 传播与取消 | 能画出一次调用的时间预算 |
| 2 | 第 5 章 | 能注册服务、解析地址、选择负载均衡策略 |
| 3 | 第 6 章 | 能解释连接复用、连接池与 drain |
| 4 | 第 7 章 | 能实现重试预算、熔断状态机和 Bulkhead |
| 5 | 第 8–9 章 | 能做限流、背压和网关错误映射 |
验收信号:下游延迟升高时,调用方的重试不会放大流量;熔断打开后请求快速失败;网关返回的错误能对应到明确的原因码。
路径三:把系统跑稳(约 6 周)
适合负责上线、值班、容量和故障复盘的人。主线是「先看得见,再改得动,最后能验收」。
| 周 | 章节 | 目标 |
|---|---|---|
| 1 | 11.1 指标口径与 Prometheus 文本 | 能定义请求量、错误率、延迟直方图 |
| 2 | 11.2 结构化日志与 traceparent | 能用请求 ID 串起客户端与服务端 |
| 3 | 第 12 章 | 能做版本兼容、灰度路由和契约测试 |
| 4 | 第 13 章 | 能设计故障注入与演练 |
| 5 | 第 14 章 | 能建容量模型、设计压测、给出成本口径 |
| 6 | 第 15 章 | 能按验收清单完成 linkd-v3 接入 |
验收信号:一次故障能定位到具体请求、具体下游、具体版本;灰度可以按比例切流并回滚;容量结论有机器口径和指标,不靠单次 QPS。
十五章与四十五节
下表每一行是一章,三个链接分别是该章的三节。首节建立概念边界,后两节负责落地;时间有限时,至少读首节和该章最后一节的「常见错误」。
用一段代码确认目录没有被写错:十五章、每章三节,总数必须是 45。
package com.plume.linkd.v3.demo;
import java.util.List;
public final class StudyPlan {
public record Chapter(int number, String title, int sections) {
}
public static void main(String[] args) {
List<Chapter> chapters = List.of(
new Chapter(1, "通信协议设计方法论", 3),
new Chapter(2, "序列化、IDL 与兼容性", 3),
new Chapter(3, "RPC 调用模型与超时语义", 3),
new Chapter(4, "gRPC-Java 深入", 3),
new Chapter(5, "服务发现与负载均衡", 3),
new Chapter(6, "连接复用与连接池", 3),
new Chapter(7, "重试、熔断与隔离", 3),
new Chapter(8, "限流、背压与队列", 3),
new Chapter(9, "网关、协议转换与 BFF", 3),
new Chapter(10, "认证、授权与传输安全", 3),
new Chapter(11, "可观测性", 3),
new Chapter(12, "版本演进与灰度兼容", 3),
new Chapter(13, "混沌演练与故障注入", 3),
new Chapter(14, "容量规划与成本", 3),
new Chapter(15, "linkd 接入 libraryd", 3));
int sections = chapters.stream().mapToInt(Chapter::sections).sum();
System.out.println("chapters=" + chapters.size() + " sections=" + sections);
System.out.println("plan.ok=" + (chapters.size() == 15 && sections == 45));
}
}
预期输出:
chapters=15 sections=45
plan.ok=true
章节依赖关系
下面这张图不是「必须按顺序读」,而是「跳过前置会缺什么」:
第 1 章 契约与错误模型
└─ 第 2 章 编码与兼容性
└─ 第 3 章 调用模型、Deadline、幂等
├─ 第 4 章 gRPC-Java 深入
│ └─ 第 5 章 服务发现与负载均衡
│ └─ 第 6 章 连接复用与连接池
├─ 第 7 章 重试、熔断与隔离
│ └─ 第 8 章 限流、背压与队列
│ └─ 第 9 章 网关与协议转换
├─ 第 10 章 认证、授权与传输安全
├─ 第 11 章 可观测性
└─ 第 12 章 版本演进与灰度
└─ 第 13 章 混沌演练
└─ 第 14 章 容量与成本
└─ 第 15 章 验收与运行手册
几个具体的跳读后果:
- 跳过第 1.2 节直接读第 7.2 节,熔断器只能按 HTTP 状态码判断失败,无法区分「业务不存在」和「下游不可用」。
- 跳过第 3.2 节直接读第 7.1 节,重试预算没有时间基准,退避参数只能拍脑袋。
- 跳过第 5 章直接读第 6 章,连接池不知道地址从哪来,只能把 host 写死。
- 跳过第 11 章直接做第 14 章压测,只能看到 QPS,看不到尾延迟和错误分布。
跳章可以,跳依赖不行。
每章验收信号
不必等到第 15 章才验收。每读完一章,用下面的信号确认自己没有「看懂了但跑不通」:
| 章 | 验收动作 | 通过判据 |
|---|---|---|
| 1 | 写一个 GetBook 的成功与失败响应 | 失败响应带 reason 和 retryable,不是一段字符串 |
| 2 | 手算 Book{isbn="42", stock=7} 的字节 | 与 toByteArray() 的前缀一致 |
| 3 | 给一次调用设置 800 ms 总预算 | 单跳 300 ms,重试仍落在总预算内 |
| 4 | 关闭并重建 ManagedChannel | 旧 Channel 的调用失败,新 Channel 正常 |
| 5 | 注册两个实例并解析 | 请求分散到两个地址,不是只打第一个 |
| 6 | 观察一条连接上的并发流 | 并发调用没有按连接串行 |
| 7 | 让下游持续失败 | 熔断打开后快速失败,半开只放少量探针 |
| 8 | 超过 200 rps 持续打 | 超出部分返回 RESOURCE_EXHAUSTED,服务不崩 |
| 9 | 用 HTTP 调 9094 | 正确转换到 9093,错误码映射到 HTTP 状态 |
| 10 | 用错误 token 调用 | 返回 PERMISSION_DENIED,不泄露内部信息 |
| 11 | 查一次慢请求 | 能从指标、日志、traceparent 三处对上同一个请求 |
| 12 | 发布一个新增字段的版本 | 旧客户端不失败,回滚不需要改数据 |
| 13 | 注入 300 ms 延迟 | 调用方在预算内失败,不产生重试风暴 |
| 14 | 按固定剖面压测 | 给出吞吐、p50/p99、错误率和机器口径 |
| 15 | 按 15.2 清单逐项执行 | 四个方法、网关、健康检查、关闭全部通过 |
自测记录至少包含日期、机器口径、命令、输出和结论。没有记录的结果,过一周就不可信。
附录与速查
| 篇目 | 内容 | 什么时候用 |
|---|---|---|
| 附录 A 通信 API 速查 | gRPC、Protobuf、Metadata、Status 的常用类与方法 | 写代码时查签名 |
| 附录 B 诊断与压测命令 | lsof、netstat、tcpdump、JFR、压测参数 | 排查连接、延迟、容量问题时 |
附录不是正文的替代。正文讲「为什么这样写」,附录讲「命令和签名是什么」。
小结
- 全书 50 篇:前置 3 篇、正文 15 章 45 节、附录 2 篇。
- 五组按能力递进:契约与调用、传输与连接、治理与入口、安全与观测、演练与收口。
- 三条路径:把 RPC 用对约 3 周、把治理做起来约 5 周、把系统跑稳约 6 周。
linkd-v3四模块单向依赖,comm-api不依赖任何业务模块。- 每章都有验收信号;跳章可以,跳依赖不行。
- 目录里的 45 个链接与正文一一对应,不虚增篇数,也不留占位标题。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。