第1章 HTTP协议基础

讲HTTP外发通道,得先聊聊HTTP本身。

很多人觉得HTTP就是调接口、发请求、收响应,没啥好学的。

但你要真想做好通道处理,协议细节就是救命稻草。我个人习惯把HTTP协议当成一本“黑话手册”——搞懂了它,服务端和客户端之间的沟通才不会有歧义。

1.1 HTTP发展史:四个里程碑

先花几分钟看看HTTP是怎么长成今天这个样子的。我刚开始做后端那会儿,还用过HTTP/1.0的老古董,想想真是泪目。

版本 发布年份 核心特征
HTTP/0.9 1991 只有GET,没有请求头,响应只有HTML
HTTP/1.0 1996 加入状态码、请求头/响应头、Content-Type
HTTP/1.1 1997 持久连接、分块传输、缓存控制、Host头
HTTP/2 2015 二进制分帧、多路复用、头部压缩、服务端推送

你想想看,从只能传文本到现在能跑视频、图片、实时消息,这中间的协议演进就是一部“通道效率提升史”。

个人记忆点:HTTP/1.1的持久连接是外发通道的“基础设施”。没有它,每次请求都得重建TCP连接,高并发场景下直接崩。

我曾经在一个旧项目的日志里看到大量 Connection: close,一查发现客户端还是HTTP/1.0协议——嗯,那就是通道瓶颈的罪魁祸首。

HTTP协议发展里程碑 HTTP/0.9 1991 · 只GET HTTP/1.0 1996 · 状态码 HTTP/1.1 1997 · 持久连接 HTTPS 2000年代 · 安全通道 HTTP/2 2015 · 多路复用 核心演进: 单向→半双工→全双工

1.2 请求方法:你是来干嘛的

HTTP定义了请求方法,说白了就是告诉服务器“我想干啥”。

外发通道里最常见的就那么几个,但我发现不少新手把GET和POST混着用,出事了还一脸懵。

  • GET:获取资源。幂等,安全(不修改数据)。缓存友好。
  • POST:提交数据。非幂等,不安全(会改数据)。不缓存。
  • PUT:替换资源。幂等,但会覆盖。
  • DELETE:删除资源。幂等。
  • PATCH:部分更新。非幂等。HTTP/1.1后期引入,很多框架支持得不好。
我个人习惯:查询类接口永远用GET,写操作用POST。这样你切HTTPS、做缓存、配CDN时都省心。PUT和DELETE在外发通道里用得少,但一旦用了就得注意幂等性。

1.3 状态码:服务端到底想说什么?

状态码是服务端的“表情包”。

外发通道里,你收到一个响应,先看状态码就能猜个七七八八。

我记得有次线上告警,客户端报一堆5xx,查了一圈发现是上游网关背时读超时——那状态码就是504 Gateway Timeout。

分类 范围 典型码 含义(外发通道视角)
信息 1xx 101 协议切换,WebSocket握手用
成功 2xx 200, 201, 204 请求正常。204常用于通道探活
重定向 3xx 301, 302, 307 资源移动了。外发通道要能处理Location头
客户端错误 4xx 400, 401, 403, 404, 429 请求有问题。429是限流,通道里常见
服务端错误 5xx 500, 502, 503, 504 服务端有问题。502/504常与网关相关
注意:不要把4xx当成5xx来处理。我曾经见过一个通道配置把所有非2xx都重试,结果把被限流的429又重发了十几次——嗯,直接把对方打挂了。

1.4 报文结构:协议长什么样?

你想想看,一个请求报文到底长什么样?

说白了就是三部分:起始行 → 头部 → 消息体。

请求报文示例:

POST /api/data HTTP/1.1
Host: example.com
Content-Type: application/json
User-Agent: MyChannel/1.0
Authorization: Bearer <>

{"key": "value"}

响应报文示例:

HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 47
Cache-Control: no-cache

{"status": "ok", "data": {"id": 123}}

核心要点:

  • Content-Length:消息体长度,必须准确。外发通道里算不对长度会导致粘包或截断。
  • Transfer-Encoding: chunked:流式传输,不用等完整body。实时通道很依赖这个。
  • Host:HTTP/1.1强制要求。你写代理通道时,Host头不能改错。
避坑指南:我曾经在写HTTP透传代理时,忘了把请求头里的Host带上,结果后端一直返回400——排查了半小时才发现是Host头丢了。后来我强制在通道代码里加了个校验:如果没有Host,默认填充请求的原始地址。

报文结构看起来简单,但每个字段都可能成为通道的“坑”。

我们后面章节会反复用到这些概念,所以现在一定得把请求方法、状态码、报文结构这三块基础打扎实了。