跳转到内容

客户端与集群的通信流程

客户端从不直接与你的服务通信,它们只与集群通信。这种间接性正是关键所在——它把一连串客户端请求,在你的业务逻辑读到任何一个字节之前,转化为一份有序、已复制、持久化的日志。

本页将端到端地追踪一条消息的完整路径:建立连接、重定向到 leader、ingress、consensus、服务执行、egress。弄清每一跳发生在哪里,你就能精确知道 p50、p99 和吞吐量究竟在哪里被赢得或失去。

一条客户端消息会经历四个阶段。每个阶段都有各自的职责,也都会带来你可以推理分析的延迟。

  1. 客户端连接到任意集群节点——并可能被重定向到 leader。
  2. 消息流经 consensus 模块——完成定序与复制。
  3. 服务处理已提交的消息——确定性执行。
  4. 响应经由 egress 回流——从 leader 到客户端。

这种间接性正是其特性所在。由于每条消息在执行前都已完成定序与复制,服务看到的是一份单一的、达成共识的消息流——在每个节点上都完全一致。

以下是完整时序中的各个参与者。请记住它们;延迟预算分摊在它们所有人身上。

参与者角色
Aeron Cluster Client你的应用的客户端库
Aeron Cluster Leader Node接受写入的节点
Aeron Cluster Member Node (Follower)复制日志
Consensus ModuleRaft 实现;对条目进行定序与复制
Aeron Archive将日志条目持久化到磁盘
Service Container承载你的集群化服务
Customer Built Cluster Logic你的业务代码(撮合引擎等)

整段旅程分为四个阶段。自上而下读一遍,延迟的故事便会自行浮现。

阶段 1 —— 客户端连接(第 1-6 步)

Section titled “阶段 1 —— 客户端连接(第 1-6 步)”

客户端可以连接到任意节点。如果该节点是 follower,请求会被重定向到 leader。随后,连接本身也会被记录到日志中。

要点在于:连这件事本身也要经过日志。会话从第一次握手起就是持久化的。

阶段 2 —— 请求处理(第 7-14 步)

Section titled “阶段 2 —— 请求处理(第 7-14 步)”

这是 consensus 这一跳。leader 在本地追加请求,并并行地把它复制给各个 follower。只有当一个 quorum 确认后,才会提交。

这一阶段主导了整个延迟预算。leader 自己也计入 quorum(quorumThreshold = n/2 + 1,ClusterMember.java),所以 3 节点集群只需 一个 follower 完成追加到 archive回送 ACK 即可提交。这一来回——复制、持久化、确认——正是 p99 的栖身之处。

阶段 3 —— 服务执行(第 15-17 步)

Section titled “阶段 3 —— 服务执行(第 15-17 步)”

只有已提交的日志片段才会到达服务。执行是确定性的——相同的输入、相同的状态变更,在每个节点上都一样。

你的撮合引擎在这里运行。因为它永远只看到已提交、已定序的消息,它从不需要考虑共识、重试或复制。请让 onSessionMessage() 保持精简:这里位于热路径上,任何内存分配或阻塞调用都会直接反映在 p50 上。

阶段 4 —— 响应回到客户端(第 18-24 步)

Section titled “阶段 4 —— 响应回到客户端(第 18-24 步)”

响应并不是抄近路回到客户端。它同样要经过共识——记录日志、复制、提交,然后再投递。

因此,单个请求会穿越共识两次——进来时一次,出去时一次。这就是 exactly-once 语义和干净重放所付出的代价。

把各阶段对应到你的延迟目标,优先级便一目了然:

  • p99 主要落在阶段 2 和阶段 4——也就是两次共识往返。更快的 follower 磁盘和抖动更低的节点间链路,对 p99 的改善超过你单独在 leader 上做的任何事情。
  • p50 主要落在阶段 3——也就是你的 onSessionMessage() 逻辑。确定性加上精简的热路径,能把中位数压低。
  • 吞吐量由 consensus 模块的批处理能力决定——每次往返中它能追加并复制的请求越多,上限就越高。ingress 和 egress 都受其制约。

由于每个请求都要缴两次共识税,最划算的优化几乎总是让 quorum 往返更快——而不是优化中间只运行一次的服务代码。