网关与路由器的区别只有一条:路由器只换帧头,网关要换整个报文格式。 网关必须懂两边的语义,所以它既是瓶颈也是单点。
这是 1.9 节的最后一个设备,也是「最多义」的一个词。 「默认网关」和「协议网关」是两个不同的东西,文末会说清。
一、它改的是内容
和路由器放一起比就清楚了:
| 对比项 | 路由器 | 网关 |
|---|---|---|
| 改什么 | 只换帧头 | 换整个报文格式 |
| 懂语义吗 | 不懂 | 必须懂两边 |
| 是否透明 | 基本透明 | 不透明 |
| 故障影响 | 可以绕路 | 没有绕路 |
第二行是本质差别: 路由器只认 IP 头里的目的地址,包里装的是 HTTP 还是数据库协议,它完全不管。 网关不行——它得把一种格式「翻译」成另一种,所以它必须同时理解两边的协议。 第四行是架构上的代价:路由器挂了还能走别的路,网关挂了两边就彻底断了。 所以网关要么做冗余,要么就得接受「它是单点」这个事实。 这也是为什么现代系统尽量少用网关——能用统一协议直连,就别翻译。
二、翻译一次要付什么
拿一条 HTTP 请求翻成内部格式看:
| 项 | HTTP 写法 | 内部格式 |
|---|---|---|
| 长度 | 63 字节 | 23 字节 |
| 可读性 | 人能读 | 只有懂的人能读 |
| 扩展性 | 加个头就行 | 要改协议 |
第一行那个「23 比 63 小」很容易让人以为「翻译就是压缩」:不是的。 它小是因为内部格式省掉了大量冗余(重复的头名、可省的空白), 但那 23 个字节里的每个位置都是硬约定,改一个字节就得两边一起改。 第三行才是真正的代价: HTTP 加一个新头,不影响任何现有实现;内部二进制格式加一个字段,所有端点都要重新编译。 这就是「文本协议活得久、二进制协议跑得快」的取舍。 所以「翻译」这个词有两层意思: 一层是「换编码」(可逆、无损),一层是「换语义模型」(可能丢信息)。 网关最难的地方永远是第二层——两种协议的语义不对齐时,网关只能自己编一套折中规则, 这套规则往往就成了新的技术债。
三、「默认网关」是另一个东西
同一个词,两个完全不同的含义:
| 说法 | 指的是 | 干什么 |
|---|---|---|
| 默认网关 | 一个 IP 地址 | 出网段时的下一跳 |
| 协议网关 | 一台转换设备 | 翻译两种协议 |
| API 网关 | 一个服务 | 统一入口、鉴权、限流 |
第一行是日常配置里最常见的:你在网卡设置里填的那个「默认网关」,其实就是一个 IP, 它的作用是「本网段之外的流量都交给它」。 它干的活是路由,不是翻译——严格说它是「默认路由器」,叫「网关」是历史习惯。 第三行是今天最流行的用法: API 网关既不做协议翻译也不做路由转发,它做的是「统一入口」——鉴权、限流、日志、灰度。 这个词的语义已经漂移到「任何统一入口」了。 所以看书时遇到「网关」两个字,先判断是哪个意思: 看上下文里有没有「协议不同」「格式转换」这类词,有就是协议网关,没有多半是「默认下一跳」。
四、动手 Demo:网关做的是「协议翻译」
把一条 HTTP 请求翻成一种假想的内部格式,看字节与结构怎么变。 重点是「变短了,但变硬了」。
http = (b'GET /sensor/temp HTTP/1.1\r\n'
b'Host: iot.local\r\n'
b'Accept: */*\r\n\r\n')
inner = b'REQ|sensor.temp|read|v1'
print('HTTP 写法 %d 字节' % len(http))
print('内部格式 %d 字节 → %s' % (len(inner), inner.decode()))
print('比值 %.0f%%' % (len(inner) * 100.0 / len(http)))
back = {'REQ': 'GET', 'sensor.temp': '/sensor/temp'}
verb, path = inner.decode().split('|')[:2]
print('翻回去:%s %s' % (back[verb], back[path]))
实跑输出:
--------------------------------------------------------------------
网关做的是「协议翻译」
--------------------------------------------------------------------
**网关与路由器的区别在于「改不改内容」**:**路由器只换帧头,网关要换整个报文格式。**
下面把一条 HTTP 请求「翻译」成一种假想的内部格式,看字节怎么变:
**HTTP 写法**:63 字节
**内部格式**:23 字节 → `REQ|sensor.temp|read|v1`
项 HTTP 内部格式
-------------------------------------------
长度 63 字节 23 字节
可读性 人能读 **只有懂的人能读**
扩展性 加头就行 **要改协议**
**网关的价值就在这个「翻译」上**:**把两套完全不同的协议接起来,让两边都以为对面是自己人。**
**代价是网关必须懂两边的语义**——**它不能像路由器那样「只管转发不管内容」。**
**所以网关是瓶颈也是单点**:**它挂了,两边就彻底断了,没有绕路可走。**
**这也是为什么现代系统尽量少用网关**,**能用统一协议直连就别翻译。**
63 字节对 23 字节,只有原来的三成多:省掉的是重复的头名(Host:、Accept:)与 CRLF,
不是把内容压缩了。
注意最后三行的「翻回去」:这里能翻回去,是因为两种格式的语义能一一对应。
真实网关经常遇到对不上的情况——比如 HTTP 的 Accept 在内部格式里根本没有对应字段,
那这个信息就只能丢掉。
「翻译」最大的风险就是这种静默丢失:两边都不报错,但功能悄悄少了一块。
所以设计网关时要先列出「哪些语义必须保留」,对不上的部分要显式拒绝,而不是默默丢掉。
30 秒实验
- 给
http加一个Cookie头,看内部格式能不能表达它。 - 把
inner改成用逗号分隔,看解析代码要改几处——这就是「二进制与文本」在扩展性上的差别。 - 想一下:你家的光猫算不算网关?它到底做了什么转换?
常见误区
- 以为「网关」都是同一个东西。 默认网关、协议网关、API 网关是三回事,看上下文判断。
- 以为网关只是「更聪明的路由器」。 路由器不改内容,网关要改,所以它必须懂两边语义。
- 以为翻译一定无损。 语义对不上时信息会丢, 而且两边都不报错——设计时要显式列出必须保留的字段。
自测三问(点开对答案)
1. 网关与路由器最本质的区别是什么? —— **路由器只换帧头、不懂内容;网关要换整个报文格式,必须懂两边语义。**所以路由器可以绕路, 网关挂了就彻底断。 2. 为什么内部二进制格式比 HTTP 短得多,却更难扩展? —— **文本协议加个头不影响现有实现;二进制格式加字段要所有端点一起改。**这就是「文本活得久、 二进制跑得快」的取舍。 3. 「默认网关」到底是不是网关? —— **严格说它是「默认路由器」**:干的是路由的活,不是翻译。叫「网关」是历史习惯。下一节进入 1.10:现代网络实态——把前面这些零件拼成一张真实的网络图。
- ☐ (本节完,下一节 1.10.1 网络的构成)