3、HTTP协议核心:HTTP/1.1与HTTP/2特性、请求/响应模型、RESTful API设计原则
好,咱们进入第三讲。HTTP协议,做物联网和边缘计算的人,几乎天天跟它打交道。但说实话,很多人对它的理解停留在「发个请求,拿个数据」的层面。这不够,远远不够。
我当年在做一个智能网关项目时,设备端上报数据用的是HTTP/1.1,结果并发一上来,连接池直接爆了。后来换成HTTP/2,问题迎刃而解。你想想看,选错协议版本,代价有多大?
3.1 HTTP/1.1 的核心特性
HTTP/1.1 是1997年定稿的,到现在二十多年了。它有几个关键特性,咱们得掰扯清楚。
持久连接(Keep-Alive)
HTTP/1.0 时代,每次请求都要新建一个TCP连接,完事就断开。这效率,简直了。HTTP/1.1 引入了持久连接,默认开启。什么意思?就是多个请求可以复用同一个TCP连接。
我在项目中遇到过,有些嵌入式设备内存极小,TCP连接数有限。用持久连接后,连接数从几十个降到了两三个,设备稳定性明显提升。
管道化(Pipelining)
这个特性,说实话,是个「理论很美,现实很骨感」的东西。它允许客户端在收到上一个响应之前,就发送下一个请求。听起来能提速对吧?但实际部署中,很多中间件和服务器实现得不好,甚至直接不支持。我个人习惯是:别依赖管道化,尤其是在边缘设备上。
分块传输编码(Chunked Transfer Encoding)
服务器不知道响应体有多大时,可以用分块传输。每块前面有长度标识,最后一块长度为0表示结束。这对流式数据很有用,比如摄像头实时画面。
核心要点:HTTP/1.1 的队头阻塞问题——同一个连接上的请求必须按顺序处理。前一个请求慢了,后面的都得等着。这是它最大的痛点。
3.2 HTTP/2 的改进与特性
HTTP/2 不是简单的版本升级,它几乎是重写了。2015年发布,核心目标是解决HTTP/1.1的性能瓶颈。
二进制分帧层
HTTP/1.1 是文本协议,HTTP/2 改成二进制了。说白了,就是把请求和响应拆成更小的帧,然后在同一个TCP连接上交错发送。这解决了队头阻塞问题。
举个例子:你同时请求a.html和b.css。在HTTP/1.1里,必须等a.html传完才能传b.css。HTTP/2呢?把a.html拆成帧1、帧2、帧3,b.css拆成帧4、帧5,然后混着发:帧1、帧4、帧2、帧5、帧3。接收方再按流ID重新组装。是不是很巧妙?
多路复用
一个TCP连接上可以同时跑多个流(stream)。每个流独立,互不干扰。这对边缘计算设备特别友好——连接数少,但并发能力不降。
我的经验:在资源受限的MCU上,HTTP/2的多路复用能显著减少内存占用。我曾经把一个网关设备的TCP连接数从50降到5,CPU占用反而下降了30%。
服务器推送
服务器可以主动给客户端推送资源,不用等客户端请求。比如客户端请求index.html,服务器可以顺手把style.css和app.js一起推过去。
嗯,这里要注意:服务器推送不是万能的。推多了反而浪费带宽。我在一个项目中就吃过亏,推了一堆客户端已经缓存过的资源,结果带宽爆了。后来加了缓存检查逻辑才解决。
头部压缩(HPACK)
HTTP/1.1的头部是纯文本,重复的字段每次都传。HTTP/2用HPACK算法压缩头部,能减少50%-90%的头部开销。对于物联网设备,每次上报数据可能就几十字节,头部占一大半,压缩后效果立竿见影。
| 特性 | HTTP/1.1 | HTTP/2 |
|---|---|---|
| 数据格式 | 文本 | 二进制 |
| 队头阻塞 | 有 | 无(流级别) |
| 多路复用 | 不支持 | 支持 |
| 头部压缩 | 无 | HPACK |
| 服务器推送 | 不支持 | 支持 |
| 连接复用 | 支持(Keep-Alive) | 原生支持 |
3.3 请求/响应模型
HTTP的请求/响应模型,说白了就是「一问一答」。客户端发请求,服务器给响应。但这里面有几个细节,做边缘计算时必须搞清楚。
请求结构
一个HTTP请求由三部分组成:请求行、请求头、请求体。
POST /api/v1/sensor/data HTTP/1.1
Host: edge-gateway.local
Content-Type: application/json
Content-Length: 67
{
"device_id": "sensor-01",
"temperature": 25.3,
"humidity": 68.2
}
请求行包含方法(POST)、路径(/api/v1/sensor/data)和协议版本。请求头是键值对,传递元数据。请求体放实际数据。
响应结构
响应也类似:状态行、响应头、响应体。
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 45
{"status": "success", "record_id": "abc123"}
状态码很重要。200表示成功,201表示创建成功,400是客户端错误,500是服务器错误。我建议你在边缘设备上,对状态码做精细化处理。别只判断「是不是200」,其他一概不管。我曾经见过一个设备,服务器返回429(限流)它还傻傻地重试,结果被拉黑了。
避坑指南:在边缘设备上,HTTP请求的超时设置要合理。我见过有人设了30秒超时,结果设备在弱网环境下一直阻塞,电池半天就耗光了。建议根据业务场景,设3-10秒。
3.4 RESTful API 设计原则
RESTful API,不是标准,而是一套设计风格。做物联网平台,API设计得好不好,直接影响设备接入的效率和可维护性。
资源导向
把一切抽象为资源。设备是资源,传感器数据是资源,命令也是资源。用URL表示资源,用HTTP方法表示操作。
- GET /devices — 获取设备列表
- GET /devices/{id} — 获取单个设备详情
- POST /devices — 注册新设备
- PUT /devices/{id} — 更新设备信息
- DELETE /devices/{id} — 删除设备
我个人习惯:URL用名词复数,不要用动词。比如 /getDeviceData 这种写法,一看就是新手。
无状态
每个请求都包含所有必要信息,服务器不保存客户端状态。这对边缘计算很重要——网关设备可能随时断连,无状态设计让恢复变得简单。
统一接口
用标准的HTTP方法,不要自己发明。比如更新资源用PUT或PATCH,不要用POST加个?action=update参数。
资源表述
客户端和服务器通过资源的表述(representation)交互。常用JSON或XML。物联网场景,我强烈推荐JSON,轻量、易解析。
我的建议:设计API时,给每个资源一个唯一的ID。设备ID、数据点ID、命令ID,都用字符串或UUID。别用自增整数,否则分布式环境下你会哭的。
HATEOAS(超媒体作为应用状态引擎)
这个原则,说实话,在物联网场景下用得不多。它要求响应中包含相关资源的链接,让客户端可以「发现」API。比如:
{
"device_id": "sensor-01",
"temperature": 25.3,
"links": [
{"rel": "self", "href": "/devices/sensor-01"},
{"rel": "data", "href": "/devices/sensor-01/data"},
{"rel": "commands", "href": "/devices/sensor-01/commands"}
]
}
这样做的好处是,客户端不用硬编码URL。但代价是响应体变大,对带宽有限的边缘设备不太友好。我一般只在管理类API中用,数据上报类API不用。
3.5 边缘计算场景下的选型建议
讲了这么多,到底怎么选?我给出几条实战建议:
- 资源极度受限(MCU、几十KB内存):用HTTP/1.1,配合Keep-Alive。HTTP/2的二进制帧解析开销大,不适合。
- 中等资源(Linux网关、几百MB内存):优先HTTP/2。多路复用能减少连接数,头部压缩能省带宽。
- 高并发场景(成百上千设备同时上报):必须HTTP/2。HTTP/1.1的队头阻塞会让你崩溃。
- 弱网环境(NB-IoT、卫星链路):建议考虑CoAP(后面章节会讲),HTTP的握手开销太大。
好了,这一章的内容就到这。HTTP协议看似简单,但里面的门道不少。下一章咱们聊聊CoAP协议,看看它凭什么在物联网领域跟HTTP叫板。