跳转至

断裂点 — REST 是怎么赢的

这一章讲什么: 在 REST 登场之前,人们试过四种办法让两台机器上的程序对话—— 90 年代的 CGI、1995 年的 Java RMI、1998 年的 EJB、2000 年代初的 SOAP。 每一种都曾是当时的正确答案,每一种都撞上了一堵墙。这一章把四面墙一面一面拆开看, 看完你就知道 REST 的六条约束为什么偏偏是那六条——它们全是前任们阵亡的地方。 不需要任何基础,这是全书的入口章。

1. 顶层全景:一条三十年的时间线

  1990s              1995          1998            2000s           2000s中
    │                  │             │               │                │
  CGI ──撞墙──→ Java RMI ──撞墙──→ EJB ──撞墙──→ SOAP ──撞墙──→ REST
  每请求一个进程     只会说 Java    容器太重      XML 臃肿        Web 本身的架构
  进程耗尽内存      互操作死穴    规格锁死创新   巨头战略化        跨语言·简单·开放
    │                  │             │               │                │
  墙:进程+内存      墙:语言        墙:重量        墙:复杂度        墙:?(至今没撞穿)
    │                  │             │               │                │
    └──────────────────┴─────────────┴───────────────┴────────────────┘
                        每换一次,都是规则重写,不是补丁

  图说:每一种架构退场的原因都不同——性能、互操作、灵活性、开放性。
  REST 的六条约東(右端)恰好逐条堵住了这四种死因。

这一章在全书链条里的位置:它是全书的「为什么」。后面六章讲的标识、方法、表述、状态管理、安全、性能,全部是这张图上 REST 那一格展开后的细节。读不懂这章,「REST 好」对你来说就只是一句口号。

本章的主线例子:一个「说 Hello 的服务」的三十年

原书有一个很讨巧的贯穿设计:每一代架构,都用同一个玩具服务来演示——客户端调一下,服务端回一句 "Hello, World!"。

代际 你要写什么才能让它说 Hello 客户端要懂什么
CGI 一个 C 程序 hello.c,编译成可执行文件丢进 cgi-bin/ 目录 一个 URL
RMI 一个 Java 约定(interface)+ 实现类 + 一个注册表,共四个 .java 文件 Java 语言 + RMI 协议(RMI 自己的通信规矩)
EJB 一个远程约定 + 一个会话 Bean(一次对话期内存活的组件)+ 用 JNDI 查名字的 Servlet Java + JNDI + 容器
SOAP 一段 XML(信封 + 头 + 体),还要一份 WSDL 描述 XML 的结构和命名约定
REST GET /users/123,返回一段 JSON(一种拿花括号和中括号写数据的文本格式) 一个 URL + HTTP

这张表就是全章的骨架。下面每一节,我们都回到这一行,看这代架构「为了说一句 Hello」付出了什么代价、又在哪一步开始崩。每一代新增的概念,就是它的死因清单。

2. CGI:Web 服务器学会调外部程序(1990 年代)

解决什么问题

最早的 Web 服务器只会一件事:把磁盘上的静态 HTML 文件原样发出去。90 年代电子商务起步,人们要的是会根据请求动态生成的页面——这次查询的订单、这个用户的购物车。CGI(Common Gateway Interface,意为「通用网关」)给出的办法朴素至极:每收到一个 HTTP 请求,服务器就启动一个新的进程来跑你的程序,程序的输出就是网页1。

怎么做:一个 hello.c 走全程

原书给的 C 程序(我们这里只走主流程,不贴代码):程序开头打印一行 Content-Type: text/html,然后读出环境变量 USER——环境变量,即操作系统启动程序时塞给它的一批「名字=值」——打印一句 Hello, <USER>!。跑起来的步骤是2:

  ①  gcc -o hello.cgi hello.c        ← 编译成一个可执行文件
  ②  chmod 755 hello.cgi             ← 让 Web 服务器有权限执行它
  ③  mv hello.cgi /usr/lib/cgi-bin/  ← 放进约定的脚本目录
  ④  Apache 里配一行:ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/
  ⑤  浏览器访问 localhost/cgi-bin/hello.cgi?user=Joe  (示例省略协议头)
  ⑥  输出:Hello, Joe!

  图说:⑥ 里那个问号后面的 user=Joe,服务器通过一个叫 QUERY_STRING 的环境变量交给程序;
  如果是 POST 请求,数据体的大小放进 CONTENT_LENGTH 变量、内容从标准输入读。

值得停一下的是第 ⑤⑥ 步的机制:程序和服务器的全部约定就是一堆环境变量——操作系统启动程序时塞给它的一批「名字=值」,程序按名字去取——和一个标准输出3。 客户端 IP、请求方法(GET 还是 POST)、内容类型(随请求带的一行说明,告诉对面「我发的是什么格式」),全是以环境变量的形式传进去的。这个设计的好处是语言无关:Perl、C、Python 写的程序遵守同一套约定就能挂在同一个服务器上。CGI 一夜之间让 Web 应用成了可能——早期论坛、购物车、服务器端图片地图(点图片的不同区域跳不同页面)全是它搭出来的4。

为什么有效,又为什么失效

有效的原因:分工干净。Unix 操作系统本来就在给每个进程分独立的内存空间和 CPU 时间,CGI 干脆把「同时服务很多请求」这件事整个外包给了操作系统,Web 服务器自己不用操心「同时应付很多请求」这件难事5。

失效的原因也在这里。Web 火爆之后,一个热门站点一秒钟可能有几百个请求,那就是几百个进程。书里给了当年的硬件家底:服务器内存 8–64 MB,单核 CPU 跑 25–66 MHz,典型配置全系统最多开 256–1024 个进程;一台 64 MB 内存的服务器,同时跑的进程一过几百就开始挣扎6。更要命的不是内存,是来回切换这个开销——CPU 刚给进程 A 干一点、又得切去 B,进程太多时,大部分时间花在「切换」而不是「干活」上,史称 CPU 饿死(CPU starvation);一个进程卡死还会变成僵尸进程,管理员每天手动杀僵尸成了例行公事7。另外 CGI 记不住「上一个请求」:用户登录状态、购物车,什么都得用 URL 传参、隐藏表单字段之类的土办法,既不安全又重复查库8。

一句话记住 CGI: 它用「多开进程」换来了零开发负担,用「多开进程」的思路撞上了规模墙。

3. Java RMI:对象漂洋过海(1995)

解决什么问题

CGI 撞的是性能墙,下一个要解的却是另一堵:分布式系统写起来太痛苦——程序员要手工处理网络连接、数据的收发、内存。Java RMI(Remote Method Invocation,远程方法调用)的想法非常性感:让一个对象住在远程机器上,用起来却和本地对象一模一样。 调用 sayHello() 的时候,网络传输和「把对象压成可以传输的格式、到对面再复原」这一步,全部藏在框架里——程序员只看见一个普通方法调用9。

怎么做:四个文件说 Hello

为了那个 hello 服务,RMI 要你写四样东西,原书的代码流程走一遍10:

  ①  远程接口 Hello:声明一个方法 String sayHello() throws RemoteException
  ②  实现类 HelloImpl:extends UnicastRemoteObject,返回 "Hello, World!"
  ③  服务器 RMIServer:LocateRegistry.createRegistry(1099)
      把对象用名字 "Hello" 挂进注册表,启动
  ④  客户端 RMIClient:registry.lookup("Hello") 拿到远程引用,
      hello.sayHello() —— 看起来就是本地调用,实际发生了一次网络往返

  图说:RMI 编译器会自动生成两个「替身」:客户端的 stub(替远程对象收方法调用、
  把调用打包成网络消息)和服务端的 skeleton(收消息、分发给真正的对象)。

这套机制里有两个今天还常见的概念值得留名。注册表(registry) 就是一个电话簿:服务端把对象按名字登记进去,客户端按名字查,免得自己去记 IP 和端口;后来 JNDI 把它推广成统一的查名服务,能查 LDAP(用户目录)也能查 DNS(网络地址)11。动态类加载更激进:如果远程对象返回的类客户端本地没有,RMI 能从服务器把字节码现下载下来装进客户端的 JVM——不用重新部署客户端就能升级协议,这在当年是黑科技12。

为什么有效,又为什么失效

有效:它是纯 Java 的方案,吃尽了 JVM 的红利——自动内存管理、跨平台、字节码可移植;和同时代的 RPC(跨语言但原始)、CORBA(语言中立但复杂到没人读得懂)、DCOM(只有 Windows)比,RMI 对 Java 程序员友好得像本地开发13。

失效的死穴只有一个,但足够致命:它只会说 Java。RMI 传输对象的那套机制、动态类加载、stub/skeleton,全都假设两端是同一个 JVM。90 年代后期企业环境是多语言的——存量系统用 C、C++、Perl、Python 写,RMI 连不进它们14。书里还列了第二梯队的问题:事务、安全、多人同时访问的控制、对象生命周期这些企业级能力全得自己扛。

事务,即一组要么整组成、要么整组不做的操作——转账就不能「钱扣了、对方却没收到」。

对象图一大(比如整棵产品目录树),「把对象压成可以传输的格式再复原」这一步直接垮掉——「待办清单、咖啡杯、用户资料传得很欢;一座城市大小的 3D 场景、集装箱船就传不动了」15。Sun 自己的 Applet 实验(把 Java 程序嵌进浏览器做客户端)也死于浏览器兼容和加载迟缓,最后业界走向的折中是:服务端用 Java Servlet,浏览器端靠原生 JavaScript16。

一句话记住 RMI: 它把「远程调用」做得像本地调用,代价是把自己锁进了 Java 的房间——而企业需要的是跨语言的走廊。

4. EJB:用规格换企业级能力(1998)

解决什么问题

RMI 缺的企业级能力(事务、安全、多线程、对象池),Sun 的回答是:别自己造,按我的规格来,厂商给你做好。EJB(Enterprise JavaBeans)配合 JCP(Java Community Process,管协作)和 JSR(Java Specification Requests,管提案)建立了一个标准化市场:容器厂商(如 JBoss、WebLogic)管运行环境,服务器厂商管底层网络,两者分开开发、分开升级但互相兼容——合起来就是应用服务器,一个 40 亿美元的市场17。

怎么做:Bean 的两种寿命

EJB 把组件分成两类,规矩很清晰18:

Bean 类型 管什么 活多久
会话 Bean(session bean) 业务逻辑、和某个客户端的一段对话 不活过会话;不带会话状态的版本(如处理登录)可被任意复用,带状态的版本跟踪多步流程
实体 Bean(entity bean) 数据库里的长期数据 一个实例对应数据库一行,像缓存一样跨会话存活

我们的 Hello 服务在这里变成一个不带会话状态的 Bean(HelloWorldBean)+ 一个远程约定 + 一个去 JNDI 里按名字查到它再调用的 Servlet——前面那张表里的第三行,现在你知道它为什么这么长了19。配置也刻意和代码分开:事务属性、安全角色写在 XML 部署描述符里(比如 <transaction-type>、<security-role>),改配置不动代码20。容器还替你做了负载均衡和实例池化——池化就是预先建好一批实例备着,用完归还、下个请求复用,不必每单新建。扛流量的脏活从程序员手里交给了厂商21。

为什么有效,又为什么失效

有效:它让 Java 正式成为企业开发主流,「一次编写、随处部署」第一次有了完整的产业支撑。

失效的是重量本身。容器管理开销巨大, critics 直指它过度设计——「Servlet 自己就能把 HTTP 请求、业务逻辑、数据库、渲染全做完,何必要 EJB 那一圈」22。更结构性的问题是规格的双刃剑:标准要求厂商互操作,也把创新锁死在规格的边界内——市场变化快,规格跟得慢,满足规格的产品对快速变化的市场需求反应「缓慢、僵硬、被标准本身框住」23。于是轻量派(JavaBeans、Spring)崛起;同一股「要轻、要灵活」的劲,也把行业推向了下一条赛道:SOAP24。

一句话记住 EJB: 它用标准化换来了企业级能力,用重量和规格锁死换来了轻量派的革命。

5. SOAP:XML 的帝国(2000 年代初)

解决什么问题

这一轮要解的是 RMI 留下的那堵墙:跨语言。SOAP(Simple Object Access Protocol)的答案是 XML——一种用尖括号把内容标注出来、人和机器都能读的文本格式,任何语言都有现成的工具读它25。 消息结构统一成三部分:信封(envelope)、头(header,放元数据——描述数据本身的信息,比如发送者和时间)、体(body,放真正的数据);再配一份 WSDL(Web Services Description Language)描述你的服务长什么样,2003 年成了 W3C 推荐标准25。

SOAP 还有一个灰色但关键的胜利:穿防火墙。当年企业防火墙会拦 CORBA、DCOM 这类私有协议,但 80 和 443 端口的 HTTP 流量放行——SOAP 把 XML 塞进 HTTP 载荷里,搭了便车,也绕开了安全策略26。开发体验也不差:工具能自动在 XML 和你的语言对象(如 Java 的 Customer、C# 的 Customer)之间互转,程序员手里拿的还是对象27。为了把「大家各干各的」收编成「大家按同一套做」,安全、路由这些 SOAP 没覆盖的角落又长出了一族 WS-* 标准(WS-Security 管令牌与签名、WS-Addressing 管消息走向,连回复地址 ReplyTo、报错地址 FaultTo 都要专门的标准标签来指)28。

为什么有效,又为什么失效

有效:它第一次让「不同语言、不同厂商的系统互操作」成了主流现实——2003 年 Web 服务市场约 100 亿美元,被预测十年内超 500 亿;Microsoft、IBM、Oracle 全军押注,收购案一桩接一桩(Great Plains、CrossWorlds、PeopleSoft、BEA)29。

失效的死因也正是这场胜利带来的:巨头的拥抱改掉了 SOAP 的气质。作者的判断很直白——当 SOAP 变成「公司利益偏好的系统的代名词」,它就疏远了更广的开发者社区;Web 的根本是不可被单一实体控制的完全开放,而厂商主导的议程和 Web 的草根本性之间出现了裂缝30。更实际的技术账:全套 XML 让消息臃肿到「awkwardly heavy」,移动端等新场景要的是轻量、不记会话的 API31。帝国自己长成了它当年要取代的东西。

一句话记住 SOAP: 它用开放标准赢了互操作,又在巨头手里变成了新的封闭——臃肿、僵硬、不再属于 Web。

一个 SOAP 请求/响应对,走一遍

光看结构图不够,我们把原书的那对消息拆开走一遍,体会一下「什么叫重」。

请求消息(信封里套着头和体,头是消息追踪号,体是 GreetRequest 操作):

<soapenv:Envelope ...>
  <soapenv:Header>
    <tracking:MessageID ...>12345</tracking:MessageID>
  </soapenv:Header>
  <soapenv:Body>
    <hello:GreetRequest/>
  </soapenv:Body>
</soapenv:Envelope>

响应消息(结构对称,体里装着 GreetResponse 和那句 Hello, World!):

<soapenv:Envelope ...>
  <soapenv:Header>
    <tracking:MessageID ...>12345</tracking:MessageID>
  </soapenv:Header>
  <soapenv:Body>
    <hello:GreetResponse>
      <hello:Message>Hello, World!</hello:Message>
    </hello:GreetResponse>
  </soapenv:Body>
</soapenv:Envelope>
  图说:为了说一句 Hello,路上跑的是两个信封:信封(Envelope)套着头(Header,放
  元数据——这里是追踪号 12345)和体(Body,放真正的操作)。消息里几乎每个名字都挂着
  一长串 xmlns 前缀(xmlns:soapenv、xmlns:hello、xmlns:tracking)——这些前缀就是
  「各家自己一套规矩」的化石。

  相对 REST 版的两行,这里的每一个字都在为「可扩展」付税:追踪号、xmlns 前缀、
  信封结构、操作名——各自有用,加起来就是书里 awkwardly heavy 的样子。

消息三件套之外,还有两样东西撑起 SOAP 的生态。WSDL 负责「服务长什么样」:body 里能放什么操作、每个操作的输入输出是什么结构,全由 WSDL 和对应的 XML schema 定义——客户端工具读 WSDL 就能生成调用代码25。*WS- 标准族**负责「SOAP 没覆盖的角落」:WS-Security 往头里嵌令牌、给消息签名加密;WS-Addressing 规定消息该送去哪、从哪来、回哪去——光是「回哪去」就要专门的标签,<wsa:To>(目的地)、<wsa:From>(来源)、<wsa:ReplyTo>(回这里)、<wsa:FaultTo>(出错报这里),一个都不能少40。再配上「服务请求方、服务提供方、服务注册表」三角色和 UDDI,完成「发现—发布—绑定」的完整闭环41。

判断(我们的,不是书里的): 重不重,要和时代一起算。2003 年的企业内网里,穿过防火墙的互操作是生死问题,XML 的「重」是买安全与治理的价钱;等到移动互联网时代,买家换了——客户端从机房里的服务器换成口袋里的手机,这笔税就付不起了。同一个设计,换一拨用户,可以从正确变成错误。 这是读一切「最佳实践」都该补的一问:它当时在为谁、买什么。 如果错,会错在: 如果企业内网的集成需求其实和移动时代一样重(安全、事务、治理全都要),SOAP 的退场就只是巨头战略失误的偶然结果——但书里给出的衰落线索(臃肿、巨头化、移动端诉求)都指向前者。

6. REST:不发明新协议,直接沿用 Web(2000 年代中)

解决什么问题

就在 SOAP 登顶的同时,加州大学尔湾分校的 Roy Fielding 发表了博士论文《Architectural Styles and the Design of Network-based Software Architectures》。他是 URL 和 HTTP 规范的主要作者之一,亲眼看着商业利益把 Web 的架构撕向不同方向,于是动笔给「分布式系统的组件该怎么交互」写一份所有人都能同意的描述32。这篇论文发表后将近十年无人问津,然后一举重新定义了可扩展分布式系统,在纯理念的地面上碾压了所有替代方案,REST 大规模取代了基于 SOAP/WSDL 的接口——直到今天33。

六条约束:每一条都对应一面前任的墙

这是全章最重要的一张表。REST 的要求被作者称为约束(constraints)——不是功能清单,是六条边界34:

约束 要求 它堵死的是哪代架构的死因
客户端-服务器 界面和数据处理分开,各自独立演化 CGI 时代页面和逻辑搅在一起
无状态(服务器不记每个客户端的上文) 每个请求自带服务器需要的全部信息 CGI 用 URL 传会话状态的土办法
可缓存 响应要能标出「能不能缓存、缓存多久」 早期系统重复查库重复传
分层系统 允许代理、网关、负载均衡器插在中间,两端无感 SOAP 只能点对点、穿防火墙靠运气
统一接口 资源的标识、操作、表述全部标准化(第 2–5 章的主角) 每代架构各自发明一套调用规矩
按需代码 (可选)服务器可以临时把可执行代码(如 JavaScript)发给客户端 富客户端能力,也致敬了 Applet 的未竟之志

回到主线例子:REST 版说 Hello

  客户端发:   GET /users/123 HTTP/1.1
              Host: api.example.org
              Accept: application/json
  服务器回:   {
                "id": 123,
                "name": "John Doe",
                "email": "johndoe@example.org"
              }

  图说:没有接口文件、没有 stub、没有 XML 信封。一个 URL 指资源(像街道地址,楼里换
  住户门牌不变),GET/POST/PUT/DELETE 管动作,JSON 是资源此刻状态的一张快照。
  出错就用现成的 200 / 404 / 201 这些状态码,响应之间用超媒体链接指路(HATEOAS,第 5 章)。

对比 SOAP 版,差距是一眼可见的:同一个服务,REST 的消息就是你眼睛看到的这两段,SOAP 需要信封、头、体、一套命名约定、WSDL、WS-* 全家桶。REST 为什么赢?书里给的答案朴素:它活在 Web 自己的空间里——用 Web 的寻址方式(URI)、Web 的方法(GET/POST/PUT/DELETE)、Web 的错误码、Web 的缓存;移动应用要轻量、不记会话的接口,REST 恰好就是;于是它成了 Web 服务的既成标准(de facto standard),自 2000 年代初以来,估计已有数百万个 REST API 被创建35。

和 SOAP 正面比一次

主线例子的两版消息已经给出了感性认识,这一节把差别钉成一张表——这也是书里「两种视角看 REST」那段的路数:REST 既是 Web 本身的架构,也是 CGI/RMI/EJB/SOAP 在接口设计上的继任者42。

对比项 SOAP 版说 Hello REST 版说 Hello
寻址 操作名藏在 body 里(GreetRequest),地址只是个入口 URI 直接指资源(/users/123),地址即含义
方法 含义全在 body,HTTP 只剩运输 GET/POST/PUT/DELETE 自带含义
出错 错误也是一条 SOAP 消息,得拆开 body 才知道 现成的状态码:200 / 404 / 201
下一步 客户端要读 WSDL 才知道还能调什么 响应里的链接指路(HATEOAS,第 05 章)
消息体积 信封+头+体+命名约定 一段 JSON
耦合 客户端工具链绑死 XML/WSDL 任何能发 HTTP 的程序都能调
图说:左列每一行都是「为解决当时的问题加的层」,右列每一行都是「Web 本来就有、
直接拿来用」。REST 没有赢在某一项更强,而是赢在它不为任何一项额外收税。

书里还给了一个量级上的收束:REST 的去中心化 adoption 迅速扩展成支撑移动应用、云服务、微服务的庞大 API 生态——自 2000 年代初以来,估计已有数百万个 REST API 被创建,喂饱了 Web 和移动应用的成倍增长43。

本章的结论:这是断裂,不是进化

作者在章末点破了这一章真正的读法:CGI → RMI → EJB → SOAP → REST 不是逐步改良——每一代都在「接口设计」这件事上打破上一代定的规则、重写规则。理解这段历史的价值在于:在系统设计里,打破过去是一种建设性行为,不是破坏;而且你能提前认出「下一次断裂」长什么样——当一种架构开始为「规模、互操作、灵活性、开放性」付不起账时36。

7. 作者的判断与证据

这一章里,作者的判断和史实要分开看:

  • 有硬证据的部分:CGI 的进程模型与环境变量机制、RMI 的 stub/skeleton 与注册表、EJB 的 JSR 分工、SOAP 的信封结构与 WSDL/WS-*、REST 的六条约束——这些都有原书逐段的技术描述和代码示例支撑,属于事实层37。
  • 作者的判断(书中的口吻):「SOAP 的臃肿和巨头化导致了 REST 的胜利」——这是作者的解读,他把 REST vs SOAP 定性为「行业需要更简单的系统间接口方式」的必然38。这个解读大方向站得住,但它淡化了另一条真实存在的线索:SOAP 在企业内部集成(EAI、ESB)里其实活得比书里写的久。
  • 一个诚实的存疑:书里连说两处「几乎没有直接证据」式的类比——传真机之于 GET 的「非侵入式复制」、FTP 之于缓存(第 3 章我们会看到)——原书明说这些影响「没有直接证据」,是帮助理解的平行类比,不是史实39。读这本书要分清这两种句子。

8. 边界与局限

  • 历史是胜者视角。 这一章把 CGI/RMI/EJB/SOAP 写成「为 REST 铺路」,真实的产业史更乱:SOAP 在企业内网集成领域至今没有死绝;EJB 的遗产(容器、依赖注入)以 Spring 的形态活得很好。把这一章当「REST 为什么合理」的论证读,比当技术史读更准确。
  • 六条约束里有一条常年缺席。 「按需代码」几乎是摆设——现实中没有主流 REST API 靠服务器下发代码干活(浏览器加载 JavaScript 算半个)。后文五章实际只用到前五条。
  • 两个数字要按年代读。 服务器「8–64 MB 内存」「40 亿美元的应用服务器市场」「100 亿→500 亿的 Web 服务市场」都是当时的量级,今天全部过时——但「进程模型撞规模墙」「规格锁死创新」这类机制判断没过时。
  • Fielding 论文「十年无人问津」 是作者的说法,细节不必当真;要紧的是它提醒我们:好架构的传播速度,远远慢于好架构的价值。

9. 可带走的

本章主线例子,一行走完: 一个说 Hello 的玩具服务,在 CGI 里是一个 C 程序 + 环境变量;在 RMI 里是接口 + 实现 + stub/skeleton + 1099 端口注册表;在 EJB 里是远程接口 + 会话 Bean + JNDI 查找;在 SOAP 里是 XML 信封 + WSDL + WS-*;在 REST 里是 GET /users/123 加一段 JSON。每多一代,少一层概念,REST 那一行最短。

  1. 五种架构的死因各不相同:CGI 死于规模(进程+内存),RMI 死于互操作(只会 Java),EJB 死于重量与规格锁死,SOAP 死于臃肿与巨头化——所以别问「哪个最先进」,要问「它会不会撞上我正在长的那堵墙」。
  2. REST 的六条约束是堵墙清单,不是信仰告白:客户端-服务器、无状态、可缓存、分层、统一接口、按需代码。
  3. REST 赢的本质是零发明:它把 Web 已有的寻址、方法、状态码、缓存直接用作接口规则,所以跨语言、穿防火墙、巨头锁不死。
  4. 标准化是双刃剑:EJB 和 SOAP 都赢在规格、也输在规格——规格保证互操作,也框住创新;工程上选「轻标准 + 开放」的组合更耐久。
  5. 穿防火墙不是安全策略:SOAP 借 80/443 端口绕过安全墙是历史事实,也是历史教训——便利和漏洞常常同根。
  6. 「打破过去是建设性的」:范式更替是重写规则,不是打补丁;认出下一次断裂的信号——规模、互操作、灵活性、开放性开始付不起账。
  7. URI 是街道地址:楼里换住户门牌不变;这个比喻是第 2 章「资源命名要稳」的源头。
  8. 「按需代码」可以忘掉:它是 REST 六约束里唯一一条现实中几乎没人用的。

10. 原文地图

主题 原书章 原文位置
CGI:每请求一个进程 Breaking Points text/08-fm-introduction.txt:70(搜「individual process」) · :209(搜「one process per request」)
CGI:环境变量与 QUERY_STRING Breaking Points text/08-fm-introduction.txt:85(搜「environment variables」) · :98(搜「QUERY_STRING」)
CGI:hello.c 编译部署六步 Breaking Points text/08-fm-introduction.txt:173(搜「gcc -o hello.cgi」)
CGI:僵尸进程与 CPU 饿死 Breaking Points text/08-fm-introduction.txt:209(搜「CPU starvation」)
CGI:当年硬件家底 Breaking Points text/08-fm-introduction.txt:212(搜「8 and 64 MB」)
CGI:状态管理的土办法 Breaking Points text/08-fm-introduction.txt:215(搜「hidden form fields」)
RMI:对象跨网络如本地 Breaking Points text/08-fm-introduction.txt:224(搜「seamless passage」)
RMI:stub/skeleton 与注册表 Breaking Points text/08-fm-introduction.txt:246(搜「stub on the client side」) · :312(搜「1099」)
RMI:动态类加载 Breaking Points text/08-fm-introduction.txt:372(搜「downloaded the bytecode」)
RMI:只会 Java 的死穴 Breaking Points text/08-fm-introduction.txt:459(搜「non-Java systems」)
Applet 实验与 Servlet+JS 折中 Breaking Points text/08-fm-introduction.txt:468(搜「Applet」) · :471(搜「browser native language, JavaScript」)
EJB:JCP/JSR 与 40 亿市场 Breaking Points text/08-fm-introduction.txt:480(搜「Java Community Process」) · :483(搜「$4 billion」)
EJB:会话/实体 Bean Breaking Points text/08-fm-introduction.txt:489(搜「session beans」) · :492(搜「entity beans」)
EJB:XML 部署描述符 Breaking Points text/08-fm-introduction.txt:508(搜「deployment descriptors」)
EJB:被批过度设计 Breaking Points text/08-fm-introduction.txt:486(搜「everything necessary」) · :486(搜「over-engineered」)
SOAP:信封/头/体与 W3C Breaking Points text/08-fm-introduction.txt:664(搜「WSDL」) · :667(搜「recommended web standard in 2003」)
SOAP:穿防火墙 Breaking Points text/08-fm-introduction.txt:667(搜「ports 80 and 443」)
SOAP:WS-* 标准族 Breaking Points text/08-fm-introduction.txt:758(搜「WS-Security」)
SOAP:巨头押注与市场数字 Breaking Points text/08-fm-introduction.txt:806(搜「$10 billion」)
SOAP:与 Web 草根气质冲突 Breaking Points text/08-fm-introduction.txt:812(搜「corporate interests」)
REST:Fielding 论文与十年沉寂 Breaking Points text/08-fm-introduction.txt:818(搜「PhD thesis」) · :821(搜「nearly 10 years」)
REST:六条约束 Breaking Points text/08-fm-introduction.txt:824(搜「six qualities」) · :847(逐条解释)
REST:URI 如街道地址、HATEOAS Breaking Points text/08-fm-introduction.txt:886(搜「street address」) · :889(搜「HATEOAS」)
REST:数百万个 API Breaking Points text/08-fm-introduction.txt:908(搜「millions of REST APIs」)
范式断裂的结论 Breaking Points text/08-fm-introduction.txt:914(搜「paradigm shifts」)

  1. 出处:「CHAPTER 1 Breaking Points」第 70 段(text/08-fm-introduction.txt:70,搜「running an individual process each time an HTTP request was received」)。原文:「This was accomplished, in part, by running an individual process each time an HTTP request was received.」 ↩

  2. 出处:「CHAPTER 1 Breaking Points」第 170–197 段(text/08-fm-introduction.txt:173,搜「gcc -o hello.cgi」;:185,搜「/usr/lib/cgi-bin/」;:197,搜「user=Joe」)。原书给了完整的编译、赋权、移入 cgi-bin、配置 ScriptAlias、访问六步。 ↩

  3. 出处:「CHAPTER 1 Breaking Points」第 85 段(text/08-fm-introduction.txt:85,搜「OS environment variables」)、第 98 段(text/08-fm-introduction.txt:98,搜「QUERY_STRING」与「CONTENT_LENGTH」)。原文说明客户端 IP、主机名、请求方法、内容类型全经环境变量传入;POST 请求用 CONTENT_LENGTH 标体长、走标准输入。 ↩

  4. 出处:「CHAPTER 1 Breaking Points」第 206 段(text/08-fm-introduction.txt:206,搜「server-side image maps」)。原文列举服务器端图片地图、模拟网店(会话、购物车、结账表单)、简单论坛等 CGI 应用。 ↩

  5. 出处:「CHAPTER 1 Breaking Points」第 76 段(text/08-fm-introduction.txt:76,搜「process-based multitasking」)。原文:Unix/Linux 的进程式多任务给每个进程独立内存与 CPU 时间,CGI 把多进程管理「delegated to the operating system」。 ↩

  6. 出处:「CHAPTER 1 Breaking Points」第 212 段(text/08-fm-introduction.txt:212,搜「8 and 64 MB of memory」与「25 MHz and 66 MHz」)。原文给了 8–64 MB 内存、25–66 MHz 单核 CPU、典型配置每用户或全系统 256–1024 进程的上限。 ↩

  7. 出处:「CHAPTER 1 Breaking Points」第 209 段(text/08-fm-introduction.txt:209,搜「context switching」与「zombies」)。原文:进程激增时 CPU 花更多时间切换而非干活;卡死的死锁进程变成僵尸,清杀僵尸成了管理员的日常手工活。 ↩

  8. 出处:「CHAPTER 1 Breaking Points」第 215 段(text/08-fm-introduction.txt:215,搜「no memory of the last one」)。原文:每个请求是孤立事件;登录状态只能靠 URL 传敏感参数、隐藏表单字段等变通办法;多步流程(向导、购物车)缺乏状态管理,还造成重复查库。 ↩

  9. 出处:「CHAPTER 1 Breaking Points」第 224 段(text/08-fm-introduction.txt:224,搜「seamless passage between remote systems」)。原文:「making a remote object look like a local one」,序列化与网络全部「under the hood」。 ↩

  10. 出处:「CHAPTER 1 Breaking Points」第 246–312 段(text/08-fm-introduction.txt:246,搜「stub on the client side」;:258,搜「interface Hello extends Remote」;:312,搜「LocateRegistry.createRegistry(1099)」)。四文件结构与原书示例一致。 ↩

  11. 出处:「CHAPTER 1 Breaking Points」第 309 段(text/08-fm-introduction.txt:309,搜「naming service」)与第 366 段(text/08-fm-introduction.txt:366,搜「Java Naming and Directory Interface」)。 ↩

  12. 出处:「CHAPTER 1 Breaking Points」第 372 段(text/08-fm-introduction.txt:372,搜「dynamic class loading」)。原文:客户端可调远程方法返回本地没有的类,RMI 从服务器托管的 codebase 下载字节码装入客户端 JVM。 ↩

  13. 出处:「CHAPTER 1 Breaking Points」第 230 段(text/08-fm-introduction.txt:230,搜「CORBA」)。原文把 RPC(跨语言但原始)、CORBA(语言中立但太复杂)、DCOM(仅限 Windows)列为被 RMI 盖过的前辈,并列出 JVM 的三大强项。 ↩

  14. 出处:「CHAPTER 1 Breaking Points」第 459 段(text/08-fm-introduction.txt:459,搜「did not play well with non-Java systems」)与第 462 段(text/08-fm-introduction.txt:462,搜「absence of an interoperable framework」)。原文:Java 之间的通信排斥了其他语言写的系统,多语言环境里集成 C/C++/Perl/Python 存量不可能。 ↩

  15. 出处:「CHAPTER 1 Breaking Points」第 243 段(text/08-fm-introduction.txt:243,搜「a to-do list, a coffee cup」)。原文:小对象(待办清单、咖啡杯、用户资料、宠物石头)传得很轻松;大东西(3D 模型、卫星、集装箱船、货运列车)序列化机制会崩。第 462 段(text/08-fm-introduction.txt:462,搜「transaction management」)列了 RMI 缺的企业级能力清单。 ↩

  16. 出处:「CHAPTER 1 Breaking Points」第 468–471 段(text/08-fm-introduction.txt:468,搜「embedded in web pages」;:471,搜「fat clients」)。原文:Applet 加载 JVM 又慢又重、浏览器兼容性糟糕,最终浏览器放弃插件模型,走向 Servlet + 原生 JavaScript 的折中。 ↩

  17. 出处:「CHAPTER 1 Breaking Points」第 480 段(text/08-fm-introduction.txt:480,搜「Java Community Process」)与第 483 段(text/08-fm-introduction.txt:483,搜「$4 billion dollar market」)。原文:JCP/JSR 治理开放协作与提案;容器厂商与服务器厂商分工独立开发、独立版本但保持兼容,「Combined, the two components became an application server and a $4 billion dollar market.」 ↩

  18. 出处:「CHAPTER 1 Breaking Points」第 489 段(text/08-fm-introduction.txt:489,搜「session beans embodied the business logic」)与第 492 段(text/08-fm-introduction.txt:492,搜「entity beans represented longer lived data」)。 ↩

  19. 出处:「CHAPTER 1 Breaking Points」第 511–649 段(text/08-fm-introduction.txt:511,搜「stateless EJB session bean」;:556,搜「uses JNDI to look up the EJB」)。HelloWorldBean、远程接口与 Servlet 的 JNDI 查找代码原书齐备。 ↩

  20. 出处:「CHAPTER 1 Breaking Points」第 508 段(text/08-fm-introduction.txt:508,搜「deployment descriptors (XML files)」)。原文:事务、安全角色、资源映射留在 EJB 代码之外的 XML 里,改配置不改代码。 ↩

  21. 出处:「CHAPTER 1 Breaking Points」第 652 段(text/08-fm-introduction.txt:652,搜「load-balance」与「pooling」)。原文:容器可在多个会话 Bean 实例间负载均衡,池化复用实例,扩缩容的活由容器厂商完成。 ↩

  22. 出处:「CHAPTER 1 Breaking Points」第 486 段(text/08-fm-introduction.txt:486,搜「everything necessary to build a complete web application」)与第 486 段(text/08-fm-introduction.txt:486,搜「over-engineered」)。原文:有专家认为 Servlet 自己就能构建完整的 Web 应用,不必背 EJB 的复杂度;批评者视 EJB 为过度设计,开销大于价值。 ↩

  23. 出处:「CHAPTER 1 Breaking Points」第 658 段(text/08-fm-introduction.txt:658,搜「conformance and agility」)。原文:标准要求厂商锁定进一份规格;满足规格的产品对快速变化的市场需求「may be slow, rigid, and bounded by the standard itself」。 ↩

  24. 出处:「CHAPTER 1 Breaking Points」第 655 段(text/08-fm-introduction.txt:655,搜「JavaBeans and Spring」)与第 658 段(text/08-fm-introduction.txt:658,搜「migrated to Simple Object Access Protocol」)。 ↩

  25. 出处:「CHAPTER 1 Breaking Points」第 664–667 段(text/08-fm-introduction.txt:664,搜「platform-neutral format」;:667,搜「recommended web standard in 2003」)。原文:SOAP 用 XML 作平台中立格式,配 WSDL,2003 年在 W3C 监督下成为推荐标准。 ↩↩↩

  26. 出处:「CHAPTER 1 Breaking Points」第 667 段(text/08-fm-introduction.txt:667,搜「ports 80 and 443」)。原文:防火墙拦 CORBA/DCOM 这类私有协议,HTTP 有开放通路;SOAP 把 XML 包进 HTTP 载荷,借已放行的端口穿墙,「bypassed security that blocked other protocols」。 ↩

  27. 出处:「CHAPTER 1 Breaking Points」第 670 段(text/08-fm-introduction.txt:670,搜「best of both worlds」)。原文:开发工具在 XML schema 与 Java/C# 对象类之间自动映射,程序员手里是原生 Customer 对象。 ↩

  28. 出处:「CHAPTER 1 Breaking Points」第 758 段(text/08-fm-introduction.txt:758,搜「WS-Security」)与第 761–800 段(text/08-fm-introduction.txt:764,搜「wsa:To」;:782,搜「wsa:ReplyTo」;:794,搜「wsa:FaultTo」)。WS-* 出自 OASIS 与 W3C,补 SOAP 没覆盖的安全与路由;原文给了 To/From/ReplyTo/FaultTo 四个寻址标签的示例。 ↩

  29. 出处:「CHAPTER 1 Breaking Points」第 806–809 段(text/08-fm-introduction.txt:806,搜「$10 billion」;:809,搜「PeopleSoft in 2004」)。原文:2003 年 Web 服务市场约 100 亿美元,预测十年末超 500 亿;三巨头的收购案清单原书逐一列出。 ↩

  30. 出处:「CHAPTER 1 Breaking Points」第 812 段(text/08-fm-introduction.txt:812,搜「corporate interests」)。原文:巨头化让 SOAP 疏远了更广的开发者社区;早期 Web 标准的核心「simplicity, neutrality, accessibility」对社区意味着更多;「a vendor led agenda revealed a disconnect between enterprise ambitions and the grassroots nature of the web」。 ↩

  31. 出处:「CHAPTER 1 Breaking Points」第 902 段(text/08-fm-introduction.txt:902,搜「awkwardly heavy」)。原文:对 XML 的完全依赖让 SOAP 显得笨重;移动应用要求更轻量、无状态的 API,REST 正好提供;REST 对资源的关注「clicked with API designers」。 ↩

  32. 出处:「CHAPTER 1 Breaking Points」第 818 段(text/08-fm-introduction.txt:818,搜「PhD thesis」)。原文:Fielding 是 URL 与 HTTP 规范的主要作者,在 UCI 观察 Web 的架构分裂,「reconciling these differences through common standards」是他写作论文的动机; hyperlink 的概念可追溯到 1965 年 Ted Nelson,Berners-Lee 1980 年在 CERN 就弄清了跨网络链接文档的思路。 ↩

  33. 出处:「CHAPTER 1 Breaking Points」第 821 段(text/08-fm-introduction.txt:821,搜「nearly 10 years」)。原文:「It was nearly 10 years after its publication that REST attracted any attention at all.」此后「redefined scalable distributed systems for the next 20 years」。 ↩

  34. 出处:「CHAPTER 1 Breaking Points」第 824–847 段(text/08-fm-introduction.txt:824,搜「six qualities」;:847,搜「define the architecture of the web」)。六条约束的原文名:client-server、stateless、cacheable、layered、uniform interface、code-on-demand;作者也称它们为「REST's constraints」。 ↩

  35. 出处:「CHAPTER 1 Breaking Points」第 902 段(text/08-fm-introduction.txt:902,搜「de facto standard」)与第 908 段(text/08-fm-introduction.txt:908,搜「millions of REST APIs」)。 ↩

  36. 出处:「CHAPTER 1 Breaking Points」第 914 段(text/08-fm-introduction.txt:914,搜「paradigm shifts」)。原文:「resists characterization as a simple evolution. Instead, it happened through distinct paradigm shifts.」结尾句:「in systems design breaking from the past is a constructive act, not a destructive one」。 ↩

  37. 出处:本章全部机制描述见原文地图所列各段;第 35 段(text/08-fm-introduction.txt:35,搜「breaking points」)交代了本章的方法论:技术的更替「characterized by technical shifts that happened after technologies reached their breaking points」。 ↩

  38. 出处:「CHAPTER 1 Breaking Points」第 902 段(text/08-fm-introduction.txt:902,搜「An adherence to the core architecture of the web inevitably led to REST's dominance」)。「inevitably」是作者的断言,不是可以复现的证据——我们在 §8 边界里说明了这一点。 ↩

  39. 出处:「CHAPTER 3 Resource Manipulation」第 133 段(text/10-fm-introduction.txt:133,搜「little evidence exists directly linking」)——原书在传真机类比处明写「Although little evidence exists directly linking fax machines to Fielding's work, conceptual parallels... help us to understand a possible link」。这是「作者自承无直接证据」式类比的直接样本。 ↩

  40. 出处:「CHAPTER 1 Breaking Points」第 758 段(text/08-fm-introduction.txt:758,搜「WS-Security」)与第 761–800 段(text/08-fm-introduction.txt:764,搜「wsa:To」;:770,搜「wsa:From」;:782,搜「wsa:ReplyTo」;:794,搜「wsa:FaultTo」)。WS-* 标准出自 OASIS 与 W3C,原书示例含四个寻址标签。 ↩

  41. 出处:「CHAPTER 1 Breaking Points」第 748 段(text/08-fm-introduction.txt:748,搜「service requestor」)。原文描绘了三角色(service requestor / service provider / service registry)与 WSDL、UDDI、SOAP 的「discovering, publishing, and binding」。 ↩

  42. 出处:「CHAPTER 1 Breaking Points」第 905 段(text/08-fm-introduction.txt:905,搜「two vantage points」)。原文:「There are two vantage points from which to view REST: fundamentally, as the architecture of the web, and through a historic lens as a successor to CGI, RMI, EJB, and SOAP for interface design patterns.」对照表的四行依据散见第 886–889 段(URI 如街道地址、标准方法、状态码与 HATEOAS)。 ↩

  43. 出处:「CHAPTER 1 Breaking Points」第 908 段(text/08-fm-introduction.txt:908,搜「millions of REST APIs」)。原文:「It is estimated that millions of REST APIs have been created since REST appeared in the early 2000s, fueling the exponential growth of web and mobile applications.」 ↩