从混沌中诞生的秩序:REST 架构演进的故事

本文基于Fielding 论文,通过AI辅助及润色而成。
https://ics.uci.edu/~fielding/pubs/dissertation/top.htm

失控的系统

1996 年,美国加州的一家大学实验室里,年轻的研究员 Evan 正忙着维护他们团队构建的网站系统。这个系统运行在 NCSA HTTPd 服务器上,使用的是 CGI 脚本与一堆 shell 管理脚本,页面是静态 HTML,数据靠 flat file 存储,偶尔用 Perl 做逻辑封装。

系统通过 HTTP/1.0 与客户端通信,但客户端与服务器之间耦合极紧,HTML 页面中嵌入表单后会直接触发一个 CGI 脚本调用,脚本处理逻辑错综复杂,还可能调用其他 shell 脚本或 socket 程序。

没有统一的 URL 结构,每次更新都要通知前端手动调整;每次功能迭代都像在泥沼中前行。前端开发人员甚至要问:“我调用 /cgi-bin/update?type=x 这个接口时,你返回的 HTML 是不是总是带三层表格嵌套?”

Evan 在日志里看到一个 IP 一秒钟内请求了 15 次相同资源,服务器拼命生成一模一样的页面,连带着文件 IO 都爆红。他知道,这不是技术人的失败,而是系统架构根本缺乏组织的表现。

他回头看了一眼那篇还没完成的博士论文草稿:“如果我不重新定义网络系统架构,这个世界就会被自己的混乱压垮。”


划定边界——Client-Server

Evan 的第一步,是引入客户端-服务器(Client-Server)约束。他将用户界面与数据存储彻底分离,前端页面只负责展现,不再处理任何业务逻辑;服务器成为唯一的数据拥有者和状态管理者。

客户端使用标准的超文本链接和表单将意图传达给服务器,而服务器专注于处理资源并生成响应表现形式。

这一约束使得开发团队开始分工协作:UI 设计者不再依赖后端逻辑细节,后端也不必关注浏览器兼容性问题。这种角色分离提升了可移植性和独立性,系统第一次显得“像个系统”。

但新问题也随之而来:客户端提交请求后,服务器还需要回忆它上次是谁、做过什么,否则就无法继续正确处理逻辑。


卸下记忆的负担——Stateless

在原先 CGI 脚本架构中,服务器通过会话文件(Session File)保存用户状态,每次请求都要读取用户 ID 文件判断当前上下文。这种状态耦合让服务器负担极重,扩展性几乎为零。

Evan 明确引入了第二个约束:无状态(Stateless)

每个 HTTP 请求必须是自足的,所有必要的上下文信息,如认证令牌、资源位置、内容偏好等,都要包含在请求中。服务器无需也不应维护客户端的会话状态。

他更新了系统设计规范:“服务器是无记忆的管道,只负责根据请求内容做出响应,而不会关心过去。”日志中原本为了状态追踪而堆积的临时文件逐渐消失,负载均衡也得以真正实现。

这一改变也带来了重要的属性:可见性。运维人员不再需要恢复会话文件来排查问题,监控系统也能直接从每次请求中看出问题所在。


减压利器——Cacheable

虽然服务器已经不再维护状态,但重复处理相同资源请求仍然消耗大量 CPU。每当有人刷新一个新闻列表页面,服务器都要读取数据、格式化并输出 HTML 表达。

Evan 决定引入第三个约束:可缓存(Cacheable)

他为所有响应添加了明确的缓存控制元数据,包括 ExpiresCache-ControlETag。客户端被允许根据这些信息判断响应是否可复用,是否需要重新验证。

这一策略源自 HTTP 的原生能力,但 Evan 将其系统性地嵌入架构之中。他写下:“缓存不是优化手段,而是协议层的基本功能。”

系统吞吐能力因此跃升一个数量级。公共资源如目录页、静态页面、用户配置甚至登录后的导航菜单都不再重复生成。系统第一次感觉“轻盈”了。


打通语言——Uniform Interface

但系统结构上依旧存在巨大不一致性。不同服务暴露不同风格的接口,有的使用表单参数,有的拼接 URL 字符串,有的甚至直接在 HTML 中硬编码行为。

Evan 在代码审查会议上做了一个决定:统一接口风格。他明确将“资源”作为系统的核心抽象,每一个资源必须被一个唯一的 URI 所标识。客户端通过获取这些资源的表现形式(Representation)来操纵系统状态。

他提出了接口统一的四个原则:资源标识、表现形式操作、自描述消息以及超媒体作为状态引擎(HATEOAS)。

“系统就像地图,而不是控制台。” Evan 说,“客户端不应预设路径,而应循着链接一步步走下去。”

前端开发者第一次意识到自己是在“浏览”而非“调用”服务,这使得系统的演化性大大提升。用户代理无需任何内部协议知识,就可以在服务引导下完成交互。


封装复杂性——Layered System

系统变得越来越复杂,团队尝试接入多个外部组件,例如身份验证服务器、日志中继器、图片代理节点等。但客户端的请求路径并不稳定:有时直接连接后端,有时绕过认证层访问缓存,有时误将调试网关暴露给终端用户。

Evan 发现,系统的复杂性必须被封装,而不是暴露。于是他引入第五个约束:分层系统(Layered System)

他重新设计系统拓扑,将其划分为清晰的层次结构:客户端、入口网关、代理服务、中转缓存、最终服务。每一层只能与其直接邻接层通信,不得窥探系统全貌。

他写道:“客户端无法知道自己所连接的节点是否是最终节点。每一层都是黑盒。”

借助这一约束,Evan 轻松在请求路径中插入认证代理、加密通道和日志网关,而客户端完全无需改动。系统拥有了透明的插拔能力,支持组件逐步部署、平滑迁移以及安全隔离。

Fielding 在论文中指出:分层使系统获得了“可中介性(mediatability)”,即允许结构在不暴露内部结构的前提下演化。

这正是 Evan 所追求的:系统复杂,但对客户端始终保持简单。


灵活的客户端——Code-on-Demand(可选)

最后,Evan 看到前端有些重复逻辑:验证表单、动态渲染字段、分页交互。他思考是否可以将这些行为下发给客户端,而不是服务器统一生成。

他引入了最后一个、也是唯一可选的约束:Code-on-Demand。服务器可以将 JavaScript 或其他解释型代码作为资源返回,让客户端动态执行,从而增强其表现能力。

Evan 并没有强制推广这一约束,而是仅在合适场景下启用。

“这是权力下放给客户端的钥匙,但并不适合所有场景。”

这一策略极大增强了客户端的表现力,减少了服务端处理负担,但也引入了新问题,如安全性与调试可见性。因此,他建议谨慎使用。


结语:约束带来自由

多年后,Evan 将这些架构演进逻辑系统性地总结在了他的博士论文中,命名为一种新的架构风格:

REST - Representational State Transfer

“这些约束不是限制开发者的创造力,而是为了让系统更有秩序,更容易理解与演化。我们要用架构原则来对抗日益增长的系统复杂性。”

从混乱、黏合的 CGI 脚本世界,到结构清晰、接口统一、演化灵活的 Web 架构,REST 是技术与哲学的结合,是互联网世界的一种秩序。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容