Hyffer
发布于 2025-08-23 / 19 阅读 / 0 评论 / 0 点赞

当需求超出原本的构想

谨此纪念我调配网络过程中那些焦头烂额的时刻

熵增

唯一永恒不变的只有变化本身,而变化对于原有的设计结构往往是破坏性的,贯穿软件产品从设计、开发、交付到维护的整个生命周期。

为此,Brooks总结道:“我从不建议全盘接纳客户的所有需求变更,而应当对其有所把关,随着开发流程的推进,把关标准也要越来越严格,否则将开发不出任何产品。”[1]

“在程序发布给顾客使用之后,它不会停止变化。发布后的改动被称为‘程序维护’,主要包含对设计缺陷的修复,也包含一些用户能察觉的新增功能。……所有修改都倾向于破坏系统的架构,增加了系统的混乱程度。用在修复原有设计瑕疵的工作量越来越少,越来越多的精力被投入到修复早期维护活动本身所引起的漏洞。随着时间的推移,系统变得越来越无序,修复工作迟早会失去根基。每一步前进都伴随着一步后退,尽管理论上系统一直可用,但实际上,整个系统已经面目全非,无法再做出改进。崭新的、基于原有系统的重新设计是完全必要的。”[1]

[1] 摘自《人月神话(The Mythical Man-Month: Essays on Software Engineering)》第11章“未雨绸缪(Plan to Throw One Away)”,有删改。节选的原文片段如下:

Far be it from me to suggest that all changes in customer objectives and requirements must, can, or should be incorporated in the design. Clearly a threshold has to be established, and it must get higher and higher as development proceeds, or no product ever appears.

A program doesn't stop changing when it is delivered for customer use. The changes after delivery are called program maintenance, but the process is fundamentally different from hardware maintenance.

Program maintenance involves no cleaning, lubrication, or repair of deterioration. It consists chiefly of changes that repair design defects. Much more often than with hardware, these changes include added functions. Usually they are visible to the user.

... All repairs tend to destroy the structure, to increase the entropy and disorder of the system. Less and less effort is spent on fixing original design flaws; more and more is spent on fixing flaws introduced by earlier fixes. As time passes, the system becomes less and less well-ordered. Sooner or later the fixing ceases to gain any ground. Each forward step is matched by a backward one. Although in principle usable forever, the system has worn out as a base for progress. Furthermore, machines change, configurations change, and user requirements change, so the system is not in fact usable forever. A brand-new, from-the-ground-up redesign is necessary.

重构:遥不可及的梦

随着需求变更和改动的积攒,软件产品的生命走到尽头——重构,它将彻底抛弃千疮百孔的历史包袱,重获新生。

然而对于网络系统而言,即使自1981年IPv4协议诞生(RFC 791)算起,至今也已44岁高龄。作为现代社会最重要的基础设施之一,它托起夜以继日奔涌着的繁忙流量,无法忍受重构所带来的巨大代价。

随着科技的发展,网络背负上更高的期待,五花八门的新需求被逐个提出,随后补丁般地悉数实现。最终,需求如山起,优雅不复存。

盘根错节

对等的破灭

在最初的IP网络设计中,节点之间是对等的,那是新生伊始未经摧残的简洁与融洽。面对IP地址耗尽的危机,NAT作为短期的临时性补救措施,像一个重量级补丁,将IP网络包裹得严严实实,伴生出私有网络,强化了对称路由。尽管全新设计的IPv6紧随其后携带着完整的解决方案问世,NAT仍凭借其极低的部署成本席卷市场[2],彻底打破IP协议的对等理念。时至今日,NAT的深远影响随处可见,而IPv6却还在艰难地推行,呕心沥血的重构尝试终究输给了立竿见影的速效止痛药。

如今,反向代理、应用层负载均衡等技术逐渐成熟,点对点通信的特性更是荡然无存。谁曾想到,获取对端的IP地址这个再平常不过的操作,有一天竟能落得如此困难[3]。当然,功能的缺失怎会难倒聪明的人们,新的补丁[4]层出不穷,一层层,一道道,缝合上需求的缺口。

[2] Network address translation - Wikipedia: By 2004, NAT had become widespread.

[3] 参考对Docker网络NAT行为的详细描述:Networking - userland-proxy could better clarify impact · Issue #17312 · docker/docs,如此复杂的SNAT情况使得容器难以获取客户端的IP地址。

[4] 比如Proxy Protocol协议:Exploring the PROXY Protocol - Benjamin Boudreau,对现有系统做微小改动,以在NAT和TCP代理环境下传递连接的信息:PROXY protocol specification: The PROXY protocol provides a convenient way to safely transport connection information such as a client's address across multiple layers of NAT or TCP proxies. It is designed to require little changes to existing components and to limit the performance impact caused by the processing of the transported information.

另辟蹊径

有段时间日常与proxychains、socksify等工具打交道,这些工具可以拦截应用发出的流量,重定向到用户指定的HTTP代理或SOCKS代理。某天我突然意识到一丝蹊跷,等等,工作在应用层的HTTP代理,为什么可以受理任何传输层的TCP连接?经过一番查阅后得知,HTTP代理在接收到HTTP CONNECT方法后,便已改名换姓,化身为TCP代理,建立起一条通往目标地址的TCP隧道[5],以此支持TLS端到端加密。(在当今HTTPS普及的背景下,TCP隧道实际上才是HTTP代理通常的工作模式[6]。)

我们总说,惊艳的设计源于优雅的抽象。至于HTTP代理建立TCP隧道这件事,究竟是巧妙计划中的一部分?还是包袱缠身下的又一次另辟蹊径?

[5] RFC 7231 - Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content, Section 4.3.6. CONNECT: The CONNECT method requests that the recipient establish a tunnel to the destination origin server ..., thereafter restrict its behavior to blind forwarding of packets, in both directions, until the tunnel is closed.

[6] HTTP代理的详细工作流程可以参考这两篇文章:Thick Client Proxying - Part 6: How HTTP(s) Proxies WorkA Primer on Proxies

层层加码,何去何从

现实的情况总是“每一步前进都伴随着一步后退”,每打上一个补丁,都会引入新的问题嗷嗷待“补”。容我再堆砌一些技术名词作佐证:为了确保客户端能够获取到网站对应的SSL证书,TLS握手报文中增加了SNI字段;然而,SNI降低了连接的私密性,于是又演进出ESNIECH

以上种种只是我所窥见的冰山一角,平静的水面下暗流涌动。演进无时无刻不在继续,就像链式反应一样,永不停歇。

船大难掉头,层层叠叠的补丁下,破旧不堪的网络托起疯长的需求。我唯有慢慢等待着技术随时间自发地更新、沉淀、淘汰。