当我还是本科生的时候,在一门名为 “计算机系统工程” 的课程中,我们阅读了许多经典的系统设计论文。我从这些论文中获得了很多乐趣和知识,但有一篇论文特别让我印象深刻,那就是 Saltzer 等人的 “系统设计中的端到端论证”。这篇论文是关于系统设计的非常一般的论述 —— 它确实探讨了几个具体系统或应用的例子,但最终阐述了端到端原则作为一种可以适用于几乎任何系统设计的视角或设计启发。
我推荐阅读这篇论文 —— 它简短且易于阅读 —— 但简而言之,它认为系统的许多功能最好在系统的 “端” 处解决,而不是在每个较低级别的接口边界处。作为一个具体的例子,论文认为,文件传输系统通过强校验和和必要时的重试来确保端到端的正确性,比坚持从其底层网络层的每个部分实现完美无损的传输要好得多。
我在软件和设计领域专业工作越久,就越发现自己反思端到端论证及其对系统设计的细微差别和影响。我想在这里承诺写下我认为特别有趣的两个不同视角。
要理解这两个视角,我们首先需要反思我们所说的 “系统” 和 “系统设计” 的含义。梅里亚姆 - 韦伯斯特 定义 “系统” 为 “一个定期相互作用或相互依赖的项目组,形成一个统一的整体”。因此,系统的本质特征是它是一个可以分解为若干子组件的单元,这些子组件可以在相对独立的情况下理解或考虑。因此,系统设计是执行这种分解的过程:决定如何将所需的系统分解为可以构建成一个功能整体的单个单元。
令人沮丧的看法:你无法组合正确性#
我们在系统设计中可能希望的一项属性是,我们可以在某种意义上,仅通过正确组合正确的子系统来实现一个正确的系统。这种希望有时被表述为渴望软件设计的 “乐高积木”:如果我们能够设计出正确、稳健、基本的软件设计构建块,我们就能够以任意方式将它们组合在一起,以低成本和努力设计复杂的系统。
端到端论证直接挑战了这种乐观。它认为,无论底层构建块的复杂性如何,我们始终必须在最上层的端到端设计层面定义和强制执行我们系统的基本正确性属性。我们不能简单地从子系统的正确性中推导出正确性:我们必须始终将其视为一种端到端属性。
因此,端到端论证也说明了抽象和系统设计中缺乏规模不变性。拥有 “端” 的系统受端到端原则的约束,并负责强制执行正确性。相反,仅作为更大整体的子系统或组件存在的系统可能没有这样的责任,并且可以将某些保证委托给其范围之外的端到端系统。
因此,这种规模依赖性暗示了世界上缺乏 “根本正确” 抽象的某种程度。如果我们想为某一类功能设计一个组件,适当的设计约束和保证取决于我们是在构建一个端到端系统还是仅仅是一个更大系统的组件。而且,如果我们在设计一个组件,我们必须提供的保证取决于顶层系统准备自己强制执行哪些属性。
在这方面,我认为对端到端论证的这种看法与泄漏抽象法则密切相关,该法则指出,所有足够复杂的抽象在某种程度上都是泄漏的。这两个原则都表达了 “带外” 交互在抽象堆栈的较高层次与底层系统的实现细节之间的某种不可避免性。
这是我在大学第一次阅读这篇论文时最强烈的感受。我当时觉得这相当令人沮丧;作为一个年轻热情的有抱负的系统设计师,我想相信存在某些理想化的柏拉图抽象,关于基本能力,如果我们能够从软件的原始以太中提取它们,就能无缝且永远提高软件设计师工作的抽象层次。端到端原则表明,世界是混乱的,尽管可能存在 “足够好” 的抽象几乎是普遍的,但我们将永远将复杂性带到系统设计堆栈的更高层次。
乐观的看法:TCB 视角#
第二种视角是我在实际系统设计中越来越欣赏的,我认为它更接近论文作者希望传达的智慧。
正确性是困难的。任何在软件领域工作过很长时间的人都很清楚,编写大多数时间都能正常工作的软件比编写始终正常工作的软件要容易得多。这种真理在大型系统中尤为真实,因为我们必须与可能具有不确定可靠性属性的外部组件进行交互。
端到端论证鼓励我们接受并拥抱这一现实。我们可以选择一些基本的正确性属性(例如:消息从 A 点复制到 B 点时未被修改;每个事务在我们的账本中恰好出现一次),并将这些属性 定位 在我们系统的一个子集(在 “端” 处)。一旦我们完成了这个设计步骤,我们就可以要求这些端到端组件的正确性,并将系统的其余部分视为一个优化问题。
我称之为 “TCB” 视角,因为我将其比作操作系统理论中的 “可信计算基础” 概念。我们寻求减少系统中直接威胁整个系统完整性的部分;完成后,我们可以在信心中开发系统的其余部分,确信错误可能表现为性能问题,甚至在整体系统中表现为可检测的故障,但不应该可能悄无声息地导致灾难性故障。
结论#
系统设计是复杂的,充满了权衡、工具、技巧和如何将复杂系统组装成组件的启发式方法。我非常喜欢端到端论证,以及关于它的这篇论文,因为我认为这是我们进行系统设计时最好的 “自上而下” 工具之一。许多技术和原则专注于 “构建”—— 从可用的实现原语中构建组件,进行组合,架构。然而,归根结底,我们的目标是用一个整体系统解决一个具体问题,而端到端论证是将这一整体目标与系统的组件和模块分解联系起来的强大工具。
这种自上而下的视角,将端到端功能与组件设计联系起来,可能会令人沮丧,因为这意味着我们可能需要在每个系统设计问题上重新审视;在两个问题之间看似相似的组件可能在最终目标上有重要不同的功能要求。然而,与此同时,它为我们提供了一个强大的工具和结构,以进行这种分解并思考系统设计。