Chapter 09
前沿与可观测性
上一章把"机器一定会挂、依赖一定会慢"当成前提,给出了一整套在故障中活下来的手段:熔断保护资源、限流削峰、重试要配退避加抖动加幂等、超时预算加舱壁止血、quorum 加 fencing token 防脑裂、混沌工程主动逼出潜伏的故障切换缺陷。本章给前八章建立的整套工具箱"标上时间戳"——哪些已是定论、哪些还在动、哪些别再学,并补上一块前面一直缺的拼图:当系统真的在线上跑起来,怎么把它看清楚。
本章建立的认知
- 把知识分成三态——稳定核心、正在变化、已被取代——按时间戳判断一个技术现在还该不该学、该不该用。
- 正在变化的几股力量(分布式 SQL、Ambient Mesh、OpenTelemetry 加 eBPF、微服务边界回潮)背后都有一个具体的经济或正确性动因,而不只是版本号在涨。
- 可观测性的骨架是 SLI/SLO/错误预算加 RED/USE 两个视角:用可测的比率给"够好"下定义,用错误预算把可靠性变成可以花的额度。
- 错误预算反直觉——预算有剩说明发布太保守,100% 可用是错误目标,因为它扼杀迭代速度。
前八章一直在回答"怎么设计",每个机制都配了代价和失效条件。但工程师在真实评审里还要回答两个文档不写的问题:这套知识里哪些已经过期了,以及系统上线之后我靠什么知道它好不好。这两件事正是本章的主题——前者是"前沿",后者是"可观测性"。它们看似无关,其实指向同一件事:让你对一个跑着的分布式系统的判断,建立在事实而非记忆和直觉上。
本章会出现不少产品名和日期,但记住版本号没有价值。每一处"正在变化"都要追到那一个驱动它的原因——为什么这个变化现在发生、它对选型决策意味着什么。读完应该能回答的是"我这个新项目该不该上服务网格",而不是"Istio ambient 哪个版本 GA"。所有日期截至 2026-06,技术演进会让这一节比其他章更快过期,这正是本章要教的元技能:给知识标时间戳。
9.1把知识三态化:稳定核心 · 正在变化 · 已被取代
一个工程师踩的最隐蔽的陷阱,不是不会某个技术,而是把一个已经过期的技术当成当下最佳实践。静态的 hash mod N 分片、跨微服务的 XA/2PC、ZooKeeper 版的 Kafka,这些在十年前的资料里都是"正确答案",今天照搬就是在给系统埋雷。判断一个技术现在的状态,需要把它放进一条时间轴上的三个区间。
- 稳定核心(stable):经过多年生产检验、机制层面没有更优替代、可以放心当地基的东西。学这些的投资回报最高,因为它们短期内不会变。
- 正在变化(in-flux):近两三年正在重塑选型的趋势。值得跟踪,但要理解驱动力而非追版本——因为它们还没尘埃落定。
- 已被取代(superseded):曾经的标准答案,今天有了机制上更优的替代。识别它们是为了不再学、不再用,并能在 code review 里指出"这是老做法"。
9.2稳定核心:前八章学到的,绝大多数都站得住
好消息是,本教程前八章的主干几乎全在稳定核心里。这些机制的根基是数学(多数派交集是集合相交,不是流行度)或被光速、网络分区这类物理与拓扑现实钉死,不会因为某个新框架出现而失效。
- CAP/PACELC(05 章):分区时在一致性与可用性间取舍、健康时在延迟与一致性间取舍——这是 Gilbert-Lynch 证明出来的,不是工程惯例,永远成立。
- 多数派 quorum 与 W+R>N(04、05 章):任意两个多数派必相交这条性质来自鸽巢原理,是一切共识安全性的地基。
- 一致性哈希 + 虚拟节点(03 章):成员变更时只搬约
1/N的 key,虚拟节点负责把负载摊匀——这是当下分区的默认做法。 - Raft(05 章):已经取代 Paxos 成为新系统的默认共识算法,etcd、Consul、TiKV、Kafka KRaft 全用它。Raft 本身从前沿沉淀成了稳定核心,这是个值得记住的演进样本。
- cache-aside + TTL(07 章)、at-least-once + 幂等(06、07 章)、指数退避 + 抖动(08 章):这些工程模式被无数生产系统验证过,是面对缓存一致性、投递语义、重试风暴的标准答案。
稳定核心的共同点是:它们解决的问题源于不会变的约束。光速不会变,所以跨地域延迟墙(01 章)一直在;网络一定会分区,所以 CAP 的取舍一直在;多数派一定相交,所以 quorum 的安全性一直在。一个技术越靠近"应对物理或数学约束",它越稳;越靠近"某种实现的工程权衡",它越可能被取代。这条标尺本身就是判断一个新技术能撑多久的工具。
9.3正在变化:四股力量与它们背后的驱动力
下面四个趋势在 2023–2025 年正在重塑选型。每一个都要追问同一个问题:驱动它的到底是什么——是新能力,还是成本,还是对旧做法的纠偏。
分布式 SQL / NewSQL:抹平 SQL 与 NoSQL 的老二分
Spanner、CockroachDB、TiDB 这类系统在 2024–2025 年走向主流。它们做的事,是把前面几章里需要工程师手工拼装的能力打包进数据库本身:自动把表的行切成一个个 range(呼应 03 章的范围分区),每个 range 用 Raft 复制到多个副本(呼应 04 章的复制与 05 章的共识),并在这之上提供全局可串行化的事务。换句话说,它把"分区 + 复制 + 共识 + 事务"这条从 03 到 06 的主线,做成了开箱即用。
最难的一环是给全球分布的事务分配一个全局有序的时间戳——没有它就无法判断两个跨地域事务谁先谁后。这里两条路线分野:
- Spanner 用 TrueTime:靠机房里的 GPS 和原子钟把时钟误差收进一个已知的不确定区间
ε。提交时执行 commit-wait——主动等待ε这么久,确保这个事务的时间戳一定早于任何之后开始的事务。代价是每次提交都要等掉这段ε(通常几毫秒)的延迟。 - CockroachDB/TiDB 用混合逻辑时钟(HLC):没有专用硬件,用物理时钟加逻辑计数器近似出因果有序的时间戳。省掉了原子钟成本,但在时钟偏移较大时要靠重试等机制兜底。
共同代价是:跨多个 range 的事务仍要走 2PC(06 章那条阻塞协议),加上 commit-wait 的延迟。所以分布式 SQL 不是免费午餐——它把复杂度从应用层挪进了数据库层,让你不必手写分库分表和 Saga,但你付出的是写延迟和对底层时钟假设的依赖。它真正的意义是抹平了"要强一致就只能用单机 SQL、要扩展就只能上 NoSQL"这个老二分。
服务网格:从每 Pod 一个 sidecar 到 Ambient Mesh
服务网格(service mesh)把限流、重试、mTLS、遥测这些 08 章讲的弹性能力从应用代码里抽出来,下沉到基础设施。经典做法是 sidecar:给每个 Pod 注入一个 Envoy 代理,所有进出流量都经过它。问题出在乘数上——一个有几千个 Pod 的集群,就有几千个 Envoy,每个都常驻几十到上百 MB 内存和一份 CPU,其中大量在空转。
Istio 的 Ambient Mesh(ambient 模式 1.24 在 2024-11 GA)重构了这个分层:
- 每个节点一个共享的 ztunnel(用 Rust 写的轻量代理)负责 L4 层的 mTLS 加密和基础遥测。一个节点上的所有 Pod 共用它,而不是一人一个。
- L7 能力(HTTP 路由、授权策略)移到 waypoint 代理,按 namespace 维度部署、按需伸缩——只有真正需要七层处理的流量才经过它。
可观测性:OpenTelemetry 成标准,eBPF 让采集零侵入
两件事正在改写"怎么把系统看清楚":
- OpenTelemetry(OTel)成了跨厂商标准:一套统一的 SDK 和协议描述 traces、metrics、logs,应用只对接 OTel,背后换 Jaeger、Prometheus、各家云厂商都不用改代码。到 2024–2025,tracing 部分已稳定,logs 部分在成熟中。它的价值是打破了厂商私有埋点 SDK 的锁定。
- eBPF 让采集零代码侵入:eBPF 是在 Linux 内核里安全运行小程序的机制,可以 hook 系统调用和 socket 读写,直接在内核层观察 HTTP/gRPC 流量,免改一行应用代码就产出 RED 指标和调用链。Grafana 的 Beyla 在 2025-05 捐给 OTel 成为 OBI(OpenTelemetry eBPF Instrumentation)。
eBPF 采集的卖点常被讲成"零侵入拿到完整的分布式追踪",这话只对了一半。L4 层的连接信息和单服务的 RED 指标(速率/错误/时延),eBPF 确实能在内核轻松拿到。但真正难的两件事它仍解决不好:跨服务的上下文传播(要把同一个请求在 A、B、C 三个服务的 span 串成一条链,需要 trace-id 跟着请求头传递,这是应用层语义,内核看不见)和 userspace TLS 加密的载荷(在用户态加密的内容内核读不到明文)。所以现实是 eBPF 把"基础指标"做到了零侵入,但"完整因果链"仍需要应用配合传播上下文。把这两者混为一谈,会在选型时高估它能省掉的工作量。
微服务边界回潮:从"什么都拆"到"按合适粒度拆"
2023–2025 年的一个明显转向是:业界不再无条件推崇"把一切都拆成微服务",转而强调把服务边界划在合适的粒度上。这个转向最常被引用、也最常被误读的,是 Prime Video 的那个案例。
流传的版本是"亚马逊放弃微服务回到单体",这是误读。真实情况是:一个团队原本用 step-function 编排一串 Lambda,各步骤之间通过 S3 互相传递数据——每一步都要往 S3 写一次、下一步再读一次,于是大量成本和延迟耗在这些 S3 往返上。他们把这条链合并成一个进程,去掉了 S3 中转。合并后的产物更像一个"粒度更粗的微服务",而不是回到了单体。教训是"把边界画对(right-sizing)",不是"微服务是错的"。
真正要警惕的反模式是分布式单体(distributed monolith):一堆名义上的"微服务"共享同一个数据库。这种结构同时付出了两份代价——它有微服务的网络延迟和部分失败复杂度(跨服务调用会超时、会失败,08 章的所有问题都在),却拿不到微服务真正的好处(独立部署、独立伸缩、故障隔离),因为共享库把它们死死耦合在一起。改一张表所有服务都得协调上线,和单体没区别,还多了一层网络的不可靠。判断一个微服务架构是否健康,一个最直接的问题是:这些服务能不能各自独立部署、各自拥有自己的数据。答案是否,就是分布式单体。
| 趋势 | 真正的驱动力 | 主要代价 | 对选型意味着什么 |
|---|---|---|---|
| 分布式 SQL | 把分区+复制+共识+事务打包,抹平 SQL/NoSQL 二分 | commit-wait 延迟、跨 range 仍走 2PC、依赖时钟假设 | 新项目要强一致+水平扩展时,先考虑它,少手写分库分表 |
| Ambient Mesh | 经济性:去掉每 Pod 一个 Envoy 的乘数 | 较新、L7 路径多一跳 waypoint、生态仍在补齐 | 规模大、sidecar 资源占用痛时值得评估;不是为了新功能 |
| OTel + eBPF | 打破厂商锁定 + 零代码采集基础指标 | 跨服务上下文传播、TLS 载荷仍难,"全链路免费"被夸大 | 埋点统一用 OTel;eBPF 补基础指标,链路追踪仍要应用配合 |
| 微服务边界回潮 | 对"无脑全拆"的纠偏,强调右尺寸 | —(这是认知纠偏,不是新技术) | 别盲目全拆;警惕共享库的分布式单体 |
一个团队把三个"微服务"接到了同一个 MySQL 上,对外宣称这是微服务架构。和真正各自独立拥有数据库的微服务相比,它多付出了什么、又没拿到什么?
展开参考答案
多付出的:服务之间的调用变成了网络调用,引入了超时、重试、部分失败这些 08 章的全部复杂度——一个服务慢会拖累调用它的服务,要熔断、要舱壁。这些在单进程里本不存在。
没拿到的:微服务最核心的两个好处都没有。① 不能独立部署——改一张共享表的 schema,所有用到它的服务都得协调发布;② 不能独立伸缩和故障隔离——共享库一旦成为瓶颈或挂掉,所有服务一起受影响。它本质是一个被网络切开的单体,即分布式单体,是两头不讨好的结构。判别标准就一句:服务能不能各自独立部署且独占自己的数据。
9.4可观测性:用 SLI/SLO/错误预算给"够好"下定义
前面所有章节都假设你知道系统现在好不好——但这个"知道"靠什么来?靠监控大盘上一堆五颜六色的曲线,并不能回答"我们达标了吗""现在该不该发版"。可观测性框架要做的,是把模糊的"系统挺稳"翻译成可测、可对账的指标。
骨架是三层递进:
- SLI(Service Level Indicator,服务等级指标):一个实测的比率。比如"成功请求数 ÷ 总请求数",或"p95 延迟低于 300ms 的请求占比"。关键是它是测出来的事实,不是愿望。
- SLO(Service Level Objective,服务等级目标):给 SLI 定的目标线。比如"99.9% 的请求成功"。SLO 是团队对自己的承诺,是工程决策的依据。
- 错误预算(error budget):
100% − SLO,即允许坏掉多少。SLO 99.9% 就意味着每月约有 0.1%(约 43 分钟)的"可以坏"的额度。坏超了就触发动作——通常是冻结发布,把精力全转到可靠性上,直到预算恢复。
大多数人的直觉是"可用性越高越好,最好 100%"。错误预算把这个直觉推翻了。如果月底错误预算还剩很多,说明这个月发布太保守、迭代太慢——预算没花完就是浪费,应该更频繁地发版、冒更多合理的险。反过来,100% 可用是一个错误的目标:追求它会让团队不敢动任何东西,把迭代速度扼杀掉,而用户从 99.9% 到 99.99% 的体感提升,往往远不值得为之放弃的产品演进速度。错误预算的真正作用,是把"可靠性"从一个含糊的道德要求,变成一笔可以理性花掉的额度,让稳定和速度之间的张力有了可量化的仲裁规则。
有了"够好"的定义,还需要知道看哪些指标。这里有两个互补的视角,覆盖问题的两端:
- RED(Rate / Errors / Duration)——面向请求:请求速率、错误率、耗时分布。站在"用户发来的请求"这一侧看服务,回答"用户感受到的服务好不好"。
- USE(Utilization / Saturation / Errors)——面向资源:利用率、饱和度、错误数。站在"CPU/内存/磁盘/连接池"这一侧看机器,回答"哪个资源快撑不住了"。
两者是因果两端:RED 告诉你"用户那头出问题了"(症状),USE 帮你定位"是哪个资源饱和导致的"(病因)。Google 的"四个黄金信号"基本是 RED 再加上一个饱和度(saturation),把请求视角和资源视角缝在一起。
这套指标不是孤立的——它正是前几章那些失败模式的探针。USE 的饱和度直接对应 07 章的背压(队列堆积、消费跟不上);RED 的错误率突增往往是 08 章熔断器跳到 OPEN 或重试风暴的信号;p99 耗时拉长常是 04 章复制延迟或某个分片热点(03 章)的外在表现。可观测性是把前八章的理论失效条件,变成线上能被看见、被告警的具体曲线。
9.5已被取代:这些别再学、别再用
识别过期技术是为了止损。下表把曾经的标准答案和今天的替代并排,每一行都对应着前面某一章讲过的机制——这也是对全书的一次回扣。
| 已被取代(旧) | 现在用(新) | 为什么换 | 对应章 |
|---|---|---|---|
| hash mod N 分片 | 一致性哈希 + 虚拟节点 | mod N 在节点数变化时几乎要重排所有 key、打爆网络冷掉缓存;一致性哈希只搬约 1/N | 03 |
| 跨服务 XA / 2PC | Saga / 发件箱(outbox) | 2PC 是按构造阻塞的协议,跨微服务持锁会拖垮线程池 | 06 |
| ZooKeeper 版 Kafka | KRaft(Kafka 内置 Raft) | 去掉外部 ZooKeeper 依赖,元数据用 Raft 自管,运维更简单、扩展性更好 | 05 |
| Hystrix | Resilience4j / 服务网格 | Hystrix 已停止维护;弹性能力下沉到网格或用更轻的库 | 08 |
| 无脑全微服务 | 右尺寸服务 / 模块化单体 | 无差别拆分带来分布式单体的复杂度却没好处 | 09 |
| 厂商私有埋点 SDK | OpenTelemetry | 统一标准打破厂商锁定,换后端不用改代码 | 09 |
选中标红的那一行(hash mod N → 一致性哈希)是这张表里最该刻进肌肉记忆的一条:它出现频率最高、踩中代价最大,也是 03 章最核心的机制。看到任何代码或设计还在用 hash(key) % N 决定数据落点,几乎都是一个该被替换的信号。
9.6往哪走:克制的前瞻
以下是趋势外推,是判断而非定论——写下来是为了给思考一个方向,不是要你照着押注。
- 弹性模式继续下沉到 sidecar / 网格:限流、重试、熔断这些 08 章的能力,越来越不该写进业务代码,而是交给基础设施统一提供。Ambient Mesh 把这条路的成本又压低了一截。
- 分布式 SQL 继续吃掉手工分库分表:随着 commit-wait 延迟优化和生态成熟,越来越多原本要手写分片+Saga 的场景会被分布式 SQL 直接覆盖。手工分库分表会越来越像一种"特殊场景下的优化",而非默认起点。
- eBPF + OTel 让可观测性更趋零侵入:基础指标的采集成本会持续下降,但前面强调过的"跨服务上下文传播"这道坎短期内仍在——零侵入会覆盖更多,但不会覆盖全部。
这些方向有一个共同主题:把分布式系统里反复出现的复杂度,从应用层不断下沉到平台层。但无论下沉到哪里,第 01 章那条主线都不会变——状态一旦跨机器存在,分区、复制、一致性的取舍就永远在那里。平台能替你实现这些取舍,但替你做不了"该牺牲什么"的决定。那始终是设计者的判断,也正是这整本教程想交到你手上的东西。
自测
先合上教程,把答案写下来或讲出来,再展开对照。能讲清"为什么"比记住名词重要。
- 一份新代码里看到用
hash(key) % N决定数据落到哪个节点。这个做法现在还该用吗?该换成什么,换的理由是什么? - 错误预算这个概念为什么说"预算有剩反而是问题"?追求 100% 可用为什么是错误目标?
- 有人说 eBPF 能"零侵入拿到完整的分布式全链路追踪"。这句话哪一半是对的、哪一半被夸大了?被夸大的部分卡在什么地方?
- 怎么一句话判断一个号称"微服务"的架构其实是分布式单体?它两头不讨好在哪?
展开参考答案
1. 不该用。hash mod N 的致命问题是节点数 N 一变(加机器或某节点挂),% N 的结果几乎对所有 key 都变了,于是几乎所有数据都要搬家——打爆网络、把所有缓存冷掉。该换成一致性哈希 + 虚拟节点(03 章):成员变更时只重映射环上相邻弧段、约 1/N 的 key 需要搬动,虚拟节点再负责把负载摊匀。
2. 错误预算 = 100% − SLO,是"允许坏掉多少"的额度。月底还剩很多,说明发布太保守、迭代太慢——预算没花完等于浪费了可以承担的合理风险,应该更激进地发版。追求 100% 可用是错误目标,因为它会让团队不敢改动任何东西,用迭代速度去换一点用户几乎无感的可用性提升;错误预算的意义正是把可靠性变成一笔可理性花掉的额度,给"稳定 vs 速度"一个可量化的仲裁。
3. 对的一半:L4 连接信息和单服务的 RED 指标(速率/错误/时延),eBPF 在内核 hook 系统调用和 socket 就能零代码拿到。被夸大的一半:完整的跨服务因果链做不到零侵入——把同一请求在多个服务里的 span 串成一条链,需要 trace-id 跟着请求头传播,这是应用层语义,内核看不见;另外用户态 TLS 加密的载荷内核读不到明文。所以基础指标零侵入、完整链路仍需应用配合。
4. 一句话判别:这些"微服务"能不能各自独立部署、各自独占自己的数据?若它们共享同一个数据库,就是分布式单体。两头不讨好:付出了微服务的网络延迟和部分失败复杂度(跨服务调用会超时会失败),却拿不到独立部署和独立伸缩/故障隔离的好处——共享库把它们死死耦合,本质是被网络切开的单体。
一个团队问你"我们要不要引入服务网格",你该先问哪 3 个问题
不要直接答要或不要。列出 3 个能决定答案的问题,并说明每个问题的"什么答案"会把决策推向"上"或"不上"。提示:从 09 章讲的驱动力(经济性、能力、纠偏)反推,一个工具值不值得引入,取决于它解决的痛是不是你真有的痛。
展开思路(不是唯一解)
问题 1 · 你现在的痛是什么?是反复在每个服务里手写重试/熔断/mTLS、想统一下沉(→ 倾向上网格,这正是它的强项);还是只是觉得"大厂都用所以该用"(→ 没有真实痛点,先别上,网格本身有显著运维和延迟成本)。
问题 2 · 你的规模到了吗、能吃下它的代价吗?网格会给每次调用加一跳代理(延迟)、增加可观测性和排障的复杂度。服务数和团队规模小的时候,这笔成本往往盖过收益。若规模大、且 sidecar 的资源占用已成痛点,可重点评估 ambient 模式(经济性是它的核心动因,09.3)。
问题 3 · 这些能力有没有更轻的替代?如果只需要重试/熔断/限流,Resilience4j 这类库或网关层就够了,不必为此背上整个网格的运维负担。把"网格"和"它声称解决的具体能力"分开问——能力可以单独获取时,未必要整套引入。
核心判断法:工具值不值得引入,取决于它解决的痛是不是你真有的痛,以及你能否吃下它的代价——而不是它有多先进。