速成课 · No. 14
每一次页面加载,机器之间都在进行一场快速而精确的对话:找到彼此的地址,开通一条线路,提出一个问题,再把答案封进一个别人都读不到的信封里。这一切都不是魔法——它只是几个清晰的想法,顶着吓人的名字。学会这些名字,整个 Web 就变得读得懂了。
只讲精髓 · 每个想法一个画面 · 掌握术语
剥掉术语,网络就是一件简单的事:机器互相发送消息。其余的一切,都是关于这些消息如何找到去路、又如何保持可靠的细节。
计算机靠传纸条来对话
一套庞大的邮政系统:任何一栋楼都能给任何另一栋楼递一张纸条,只要它在信封上写对地址。
网络不过是连接起来、好让彼此发送数据的计算机。数据以小小的数据包传送——就像被拆成一张张明信片的纸条,每一张都标着它从哪来、往哪去。互联网就是把这件事放大到行星尺度:数十亿台机器,任何一台都能把一个数据包递给任何另一台。这就是全部的根基;剩下的,是我们如何让它变得可靠而有意义。
每台机器都有一个地址——它的 IP
地球上每一栋楼都有独一无二的邮政地址。没有它,邮差就无处投递,也无处回信。
要让一个数据包抵达,目的地需要一个地址。那就是 IP 地址——一个标识机器在网络中身份的数字,比如 142.250.74.78。每台联网的设备都有一个。当你的笔记本发出一个请求时,它会盖上自己的 IP 作为回信地址,好让答案知道该回到哪里。没有地址,就没有对话。
客户端发问,服务器作答
一间餐厅和一间厨房。一边点单;另一边备好再送回来。这顿饭里,角色是固定的。
Web 的大部分都跑在客户端—服务器模型上。客户端(你的浏览器、你的应用)发出一个请求;服务器(一台运行着网站或服务的机器)送回一个响应。你的手机是客户端,问「把这个页面给我」;某处的服务器用那个页面来回答。把这一对关系记牢——下面的每个想法,都是为了让这场「请求与响应」运转起来。
网络就是机器在传递写好地址的消息。客户端发问,服务器作答——其余的一切,都是让这件事变得可靠。
你敲下一个名字,比如 example.com,可网络只会把路由指向数字。总得有什么东西,把你记得住的名字翻译成机器需要的地址。那个东西就是 DNS。
名字是给人的;数字是给机器的
你靠名字记住朋友,可电话系统只拨号码——于是你留着一份联系人名单,把一个变成另一个。
人们记得住 wikipedia.org;网络需要一个像 198.35.26.96 这样的 IP 地址。域名是对人友好的标签;IP 是对机器友好的地址。这两者必须被关联起来,因为没人愿意背号码,也没有路由器能按名字来路由。弥合这道鸿沟的,是一整套系统,它在每一次点击中不停地、隐形地运转着。
DNS 是互联网的电话簿
一本巨大的、永远开着门的名录:你给它一个名字,它把要拨的号码递回给你。你从不见它干活——你只是拿到了号码。
DNS——域名系统——就是把域名翻译成对应 IP 地址的服务。在你的浏览器能去取 example.com 之前,它先问 DNS「这个名字的地址是什么?」,然后拿回一个数字。这次查询(也叫解析)每一次都先发生,只在毫秒之间。正是这个并不光鲜的步骤,让人类读得懂的网址成为可能。
答案会被缓存,下一次就快了
你查一次朋友的号码,把它记在便利贴上——下一次你只要看那张贴纸,而不必再翻开整本名录。
每个名字都从头查一遍会很慢,所以 DNS 的答案会被缓存——由你的浏览器、你的操作系统和你的网络记住一阵子。地址附带一个 TTL(存活时间),说明在再次核对之前,它可以安全复用多久。这就是为什么你访问过的站点能瞬间载入它的地址,也是为什么一次 DNS 变更需要时间才能扩散开来。
DNS 就是电话簿:它把你记得住的名字变成网络需要的 IP 地址,再把它缓存起来,好让下一次瞬间完成。
知道地址,和真正展开一场对话不是一回事。在消息可靠地流动之前,两台机器先开通一条连接——并约定好该敲服务器的哪一「扇门」。
TCP 是一条可靠、有序的线路
一通带确认的电话:你说一句话,对方说「收到」,在每一部分都被确认并按序到位之前,什么都不会往下走。
TCP(传输控制协议)是可靠发送数据最常用的方式。它通过一次快速的握手开通连接(双方都同意对话),然后保证每个数据包都按序到达,丢失的会被重发。你拿一点速度,换来「没有任何东西被丢弃或打乱」的保障。Web、电子邮件,以及大多数应用,正是为了那份可靠才跑在 TCP 上。
UDP 用可靠性换速度
在嘈杂的房间里大声喊出最新消息——有些词会丢,但你不会停下来逐字确认,因为跟上节奏比抓住每一个字更要紧。
UDP 是那个快、却不作保证的替代方案:它不握手就把数据包打出去,丢了也不重发。你失去可靠性,但换来速度和低开销,对直播视频、语音通话和游戏来说,这正是对的取舍——掉一帧无所谓,但等待不行。知道这两者都存在,就明白网络里始终有这么一个「速度对可靠」的旋钮。
端口是机器上编了号的门
一栋楼,许多门,每扇门都为某个用途编了号——送货走 80 号门,办公室走另一扇。同一个地址,不同的入口。
一台机器跑着许多服务,所以光有 IP 地址还不够——你还需要一个端口,一个挑出该找哪个服务的数字。按惯例,Web 流量用端口 80(HTTP)或 443(HTTPS);数据库可能监听另一个端口。地址把你带到那栋楼;端口把你带到对的那扇门。IP 加端口合在一起,才点名出一个确切的目的地。
TCP 是一条可靠、有序的线路;UDP 又快又会丢包。IP 找到那栋楼,端口挑出那扇门。
线路一旦开通,双方就需要一种共同的语言来发问和作答。在 Web 上,这种语言就是 HTTP——一种为每一次请求和回复定下的、简单而严格的格式。
HTTP 就是请求与响应
一来一回的正式信件:一封结构清晰的请求寄出去,一封结构清晰的回复寄回来,各自都遵循同一套约定好的格式。
HTTP(超文本传输协议)是 Web 的语言。客户端发出一个请求——它想要什么——服务器送回一个响应——结果。每一个页面、每一张图片、每一次 API 调用,都是这样的一次往来。它基于文本、有结构,正因如此,这么多工具才都能说它。把一次请求和响应的形态掌握透,你就懂了整个 Web 是怎么流动的。
方法说明你想做什么
在图书馆柜台:你可以借一本书、还一本书,或者请求添一本新的。同一个柜台,不同的意图——工作人员对每一种的处理也不同。
每个请求都有一个方法——那个动词。GET 取数据(载入一个页面)。POST 送新数据(提交一个表单)。PUT 更新某样东西;DELETE 删除它。方法让服务器一眼看出你的意图,行为端正的系统会区别对待它们——GET 永远不该改动任何东西,而 POST 被预期会。这些动词是 API 设计方式的脊梁。
头部承载细节;主体承载货物
一个信封,外面写着些备注——给谁的、里面是什么、该怎么处理——以及里头那封真正的信。
每个请求和响应都有头部——一行行带标签的元数据,比如内容类型、语言、谁在发问,还有认证令牌——以及一个可选的主体,也就是真正的载荷(往上送的表单数据,回来的页面或 JSON)。头部是双方在不碰货物的情况下,协商各种细节的方式。一次往来里大部分的「怎么做」,都住在头部里。
状态码告诉你结果如何
给回复用的红绿灯:绿灯放行,重定向指向别处,红灯告诉你哪里出了错——以及是谁的错。
每个响应都带一个状态码。2xx 表示成功(200 OK)。3xx 是重定向(去那边看看)。4xx 表示客户端错了——404 Not Found、403 Forbidden——是你问错了。5xx 表示服务器错了——500——是它坏了。读第一个数字,你立刻就知道它有没有成功、是谁的错、该往哪儿看。这是 Web 上最有用的那一项调试技能。
HTTP 在两次请求之间就把你忘了
一个对你上次到访毫无记忆的柜台办事员——你每次走上前,都得重新自我介绍,再把票据出示一遍。
HTTP 是无状态的:每个请求都独自成立,默认情况下服务器对上一个请求什么都不记得。所以要保持登录,每个请求都必须带上「你是谁」的凭证——头部里的一个 cookie 或一枚令牌。正是无状态,让 Web 能扩展到数十亿次请求,而理解了它,就能解释为什么你的身份会随着每一次调用一起捎带过去。
HTTP 就是请求与响应:用方法表意图,用头部装细节,用主体载货物,再用一个状态码精确告诉你结果如何。
朴素的 HTTP 是一张明信片——任何经手的人都能读到。HTTPS 是同一场对话,封进一个只有对的那台服务器才能打开的信封里,还签了名,好让你知道在跟谁说话。
加密把消息封起来
把一张读得到的明信片,换成一个只有收件人握着钥匙的上锁箱子——快递员一路搬着它,却没有一个人能读到里面装的什么。
HTTPS 就是用 TLS 包了一层加密的 HTTP。它把内容打乱,让你和服务器之间的任何人——你咖啡馆的 Wi-Fi、一家互联网服务商、一个攻击者——只看到一堆乱码,而不是你的密码或消息。地址栏里那个锁形图标,意味着这道封印就位了。在现代 Web 上,它是默认设置,因为一场没封上的对话,就是一场任何人都能读的对话。
证书证明你在跟谁说话
一本由受信任的权威机构查验过的护照——它证明这个人正是他所声称的那个人,而不是一个顶着他名字的冒充者。
光有加密还不够;你还需要知道那台服务器真的是 yourbank.com,而不是个冒牌货。证书是一份数字身份证,由受信任的证书颁发机构签发,它证明一台服务器拥有它所声称的那个名字。你的浏览器会自动核对它,并在它缺失或不对时向你发出警告。正是这一点,挡住了攻击者——哪怕是在一条加密连接上——冒充某个站点。
为什么那把挂锁要紧
这是两件事的区别:寄一封封好、且经过验证的信,和在挤满人的房间里大声喊出你的银行账户信息。
没有 HTTPS,网络路径上的任何人都能读取、甚至篡改你的流量——一个man-in-the-middle,悄无声息地坐在你和服务器之间。有了它,消息被封上,发件人也被验证。这就是为什么登录页、支付,乃至如今几乎一切,都用 HTTPS,也是为什么浏览器把朴素的 HTTP 标记为「不安全」。那把挂锁是个小图标,代表着一份很大的保证。
HTTPS 用加密把消息封起来,用证书证明服务器的身份——隐私与信任,都在一把挂锁里。
Web 感觉起来是即时的,可距离和物理定律始终在收过路费。两个词——延迟和带宽——就解释了大部分「为什么快、为什么慢」。
延迟是等待;带宽是宽度
一根水管:第一滴水多久才到,是一回事;每秒能流过多少,是另一回事。再宽的管子,水也要先有个延迟才能流到那一头。
延迟是响应开始前的那段等待——一条消息来回往返的时间。带宽是你每秒能搬动多少数据。它俩相互独立:一条连接可以是高带宽,却又是高延迟。大文件下载在乎带宽;一个利落、灵敏的站点在乎延迟。把这两者搞混,正是为什么「我的网很快,可这站点感觉很慢」会让人摸不着头脑。
每一次往返都要花钱,而距离是真实的
向另一块大陆上的某个人问一个问题:哪怕以光速,这一来一回也有一个你打不破的下限。
一个发往遥远服务器的请求,要做一次物理上的往返,而即便是光,跨越行星也并非即时——一趟横跨世界的往返,有一个数十毫秒的硬下限。更糟的是,载入一个页面往往需要许多次往返。这就是为什么话痨式的设计在距离面前显得迟缓,也是为什么「减少往返次数」是存在已久的性能技巧之一。
用 CDN 把答案放得更近
一本热门书,被各地的本地图书馆都备上货,而不是只有一座你必须寄信去借的中央档案馆——离你近的那本,到得快得多。
CDN(内容分发网络)是一支遍布世界各地的服务器队伍,把你内容的副本放在离用户近的地方。当东京的某人载入你的站点时,他是从附近的一座城市得到服务的,而不是从隔着一片海的你的源服务器——这把延迟大幅砍掉。CDN 就是把缓存用到了地理上,全球性的站点之所以在哪儿都感觉像本地的,靠的就是它。
负载均衡器把工作分摊开
银行里的排队管理员,把顾客引向任何一个空着的柜员,于是没有哪一个窗口被挤爆,而别的窗口闲着。
当一台服务器扛不住所有流量时,一个负载均衡器就坐在好几台前面,把请求分摊到它们身上。大站点正是靠它来应对数百万用户——在均衡器后面加更多服务器——也靠它在一台服务器宕机时撑下去,因为均衡器只要绕开它来路由就行。这个词精确地描述了它干的事:它在均衡负载。
延迟是等待;带宽是宽度。距离总在收过路费——CDN 把答案放得更近,负载均衡器把工作分摊开。
现在,跟着一次点击——从地址栏到渲染出的页面——把每个词都串到一起。如果你能把这个讲出来,你就懂了 Web 是怎么对话的。
当你载入一个 URL 时,发生了什么
一封信完整的旅程:查地址、开通线路、封好信封、提出问题、拿到回复——全都在远不到一秒之间。
你敲下 example.com。DNS 把名字变成一个 IP 地址。你的浏览器在端口 443 上向那个 IP 开通一条 TCP 连接,双方做一次 TLS 握手,好让线路被加密、服务器的证书被核对。在那条封好的线路上,你的浏览器送出一个 HTTP GET 请求;服务器用一个状态码和一个主体来回复。页面可能来自附近的一个 CDN,以削减延迟。你的浏览器读取响应,把页面画出来。这门课里的每一个术语,都不过是给那平凡的一秒中的某一步命了名。
- DNS 把域名解析成一个 IP 地址。 - 一条 TCP 连接在某个端口上向那个 IP 开通(HTTPS 用 443)。 - 一次 TLS 握手把线路加密,并核对服务器的证书。 - 客户端送出一个 HTTP 请求——一个方法、若干头部,也许还有一个主体。 - 服务器返回一个状态码、若干头部和一个主体。 - 一个 CDN 可能就近提供它;一个负载均衡器可能挑出那台服务器。
- IP 地址 / 数据包 —— 机器的地址,以及数据赖以传送的那些小块。 - DNS / TTL —— 名字到数字的查询,以及答案被缓存多久。 - TCP / UDP / 端口 —— 可靠的线路、快速的线路,以及机器上的哪扇门。 - HTTP / 方法 / 头部 / 主体 / 状态码 —— 那门请求-响应的语言。 - HTTPS / TLS / 证书 —— 那个封好、经过验证的信封。 - 延迟 / 带宽 / CDN / 负载均衡器 —— 等待、宽度,以及规模如何对抗这两者。
- 一个 404 对上一个 500,立刻就告诉你是你、还是服务器的错。 - 「网很快但站点很慢」读作一个延迟问题,而非带宽问题。 - 那把挂锁意味着既加密又身份已验证——你知道它保证了什么。 - 你能解释为什么保持登录,需要在每一个无状态请求上都带一个 cookie。 - 你能不看就把上面那整段 URL 旅程讲出来。
载入一个页面,就是一场快速的对话:找到地址,开通线路,封好它,发问,作答。每一个吓人的词,都不过是那一秒里的某一步。