跨网络通信编程模式
RESTFUL API
虽然HTTP协议已经规范了基础的请求/响应格式,但正是因为它太灵活,反而导致了早期Web服务的混乱。
HTTP原生问题: 同一个用户查询功能,不同开发者可能设计出完全不同的接口
所以导致了 RESTFUL API的诞生!
约定:
URL 表示资源,如 /users/1
用标准 HTTP 动词:GET 查,POST 新增,PUT 修改,DELETE 删
数据统一用 JSON(或标准格式)
服务无状态(每次请求都完整表达意图)
规范为
HTTP/1.1 201 Created
Location: /users/123
Content-Type: application/json
{ "id": 123, "name": "Alice" }
// 传统混乱的响应
{
"success": true,
"code": 200,
"message": "OK",
"data": { "id": 123, "name": "Alice" }
}
也就是说:
HTTP 可以发任何东西,RESTful API 让它发得标准、发得规范。
注意:
RESTful API 规范是靠人遵守的工程文化,不是自动生效的。
gRPC
基于 HTTP/2协议 一个高性能 RPC 框架,使用_Protocol Buffers(protobuf)_作为接口定义语言(IDL)和数据序列化工具。
HTTP/2
因为HTTP/1.1出现了问题_队头阻塞_
虽然HTTP/1.1支持长连接,复用TCP连接,处理多个请求,但是: 同一时刻只能处理一个请求,等响应回来才能发下一个(串行处理)
HTTP/2 使用 二进制帧。
将请求/响应拆分为更小的 帧(Frame)
一个 TCP 连接可_并行_传输多个请求/响应,避免队头阻塞。
主动推送
服务器可以预测客户端需要的资源,在响应主请求时一并发送,服务器可以 主动推送资源(如 CSS、JS)给客户端,无需等待请求。
推送承诺(PUSH_PROMISE):先告知客户端将要推送哪些资源
缓存消化:客户端可以拒绝已缓存的推送
客户端可以指定流的优先级(如优先加载 HTML,再加载图片)。
每个帧带有 流 ID(Stream ID),客户端和服务器可以_乱序发送和重组数据_。
二进制帧
把一个完整的消息拆分为多个标准的帧,帧的内容可能包括
-
HEADERS帧 - 包含HTTP头信息
-
DATA帧 - 包含消息体(如HTML内容)
-
SETTINGS帧 - 连接配置参数
-
PRIORITY帧 - 流优先级
-
PUSH_PROMISE帧 - 服务器推送预告
每个帧都有固定的9字节头部:
+-----------------------------------------------+ | Length (24 bits) | 帧负载长度 | Type (8 bits) | 帧类型 | Flags (8 bits) | 标志位 | R (1 bit) | Stream Identifier (31 bits) | 流ID +-----------------------------------------------+ | Frame Payload (0~16,777,215 bytes) | 帧内容 +-----------------------------------------------+
流思想
一个流代表一个完整的请求-响应交换,
每个流有唯一的ID标识
一个TCP连接可以同时承载多个流
####HTTP/2 如何并行
物理层:只有一条TCP连接
逻辑层:通过流ID(Stream ID)创建多个虚拟通道
工作方式:不同流的帧可以交替发送,接收方根据流ID重组
在有序的TCP字节流之上构建无序的帧传输
通过流ID实现逻辑分离
请求流程
假设浏览器请求一个网页和其关联的CSS文件:
客户端发起流1(HTML请求):
发送HEADERS帧(包含GET /index.html)
服务器响应流1:
发送HEADERS帧(HTTP 200 OK)
发送多个DATA帧(HTML内容分块)
服务器主动推送CSS(流2):
发送PUSH_PROMISE帧(预告将推送/style.css)
发送HEADERS帧(HTTP 200 OK)
发送DATA帧(CSS内容)
所有这些帧在同一个TCP连接上交错传输
HPACK 压缩算法
用索引代替重复的字符串,减少传输量
传统HTTP1.x的头部问题:
GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: text/html,application/xhtml+xml
Accept-Language: zh-CN,zh;q=0.9
Cookie: session_id=abc123; theme=dark
重复传输:每次请求都发送几乎相同的头部字段
纯文本格式:占用空间大且不易压缩
增长趋势:现代网页平均请求头已达1.5KB+
静态表编码:
:method: GET → 只需发送0x82(二进制10000010)
动态表编码:
哈夫曼编码:
如何利用HTTP2
- 多个 gRPC 调用可以在同一个 TCP 连接上并行传输,避免 HTTP/1.1 的队头阻塞。
- Protocol Buffers(protobuf)本身就是二进制格式,与 HTTP/2 的二进制帧天然契合。
- HTTP/2 的流式特性使得 gRPC 的 Server Streaming、Client Streaming 和 Bidirectional Streaming 成为可能。
- 头部压缩(HPACK)减少了 gRPC 调用的元数据开销。
Protocol Buffers
IDL 与编程语言无关的类型系统,用于明确定义服务接口。IDL定义了:
-
服务提供哪些功能(方法)
-
每个方法的输入输出结构
-
数据类型和字段约束
数据序列化:
将内存中的对象转换为可以存储或传输的二进制/文本格式的过程
// Go结构体
user := User{
ID: "123",
Name: "Alice",
Age: 30,
}
// 序列化为protobuf二进制格式
data, _ := proto.Marshal(&user)
// 得到紧凑的二进制:0x0a036132... (实际二进制数据)