流式 HTTP 是指在一次 HTTP 请求/响应过程中,服务端或客户端能够逐步发送或接收数据,而不是等待整个内容准备好后一次性传输。这种模式适用于大文件传输、实时日志推送、SSE(Server-Sent Events)、视频流等场景。
下面我们将从 HTTP/1.1、HTTP/2、HTTP/3 三个协议版本的角度详细解释流式传输的机制,并结合 Go 语言 编写服务端示例代码,说明其原理和实现方式。
一、HTTP/1.1 的流式传输
1.1 原理
- HTTP/1.1 支持 Chunked Transfer Encoding(分块传输编码)。
- 服务端可以在不知道内容总长度的情况下,通过
Transfer-Encoding: chunked头部逐块发送响应体。 - 每个块以十六进制长度开头,后跟
\r\n,然后是数据,再以\r\n结尾。 - 最终用一个长度为 0 的块表示结束。
1.2 Go 实现示例(HTTP/1.1 流式响应)
package main
import (
"fmt"
"log"
"net/http"
"time"
)
func streamHandler(w http.ResponseWriter, r *http.Request) {
// 设置响应头,启用分块传输(无需显式设置,Go 自动处理)
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK)
// 获取底层的 flusher,用于强制刷新缓冲区
flusher, ok := w.(http.Flusher)
if !ok {
http.Error(w, "Streaming unsupported", http.StatusInternalServerError)
return
}
for i := 1; i <= 5; i++ {
fmt.Fprintf(w, "Chunk %d\n", i)
flusher.Flush() // 立即发送已写入的数据
time.Sleep(1 * time.Second)
}
}
func main() {
http.HandleFunc("/stream", streamHandler)
log.Println("Server listening on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
✅ 注意:必须使用
http.Flusher接口调用Flush(),否则 Go 的默认缓冲区会延迟发送数据。
二、HTTP/2 的流式传输
2.1 原理
- HTTP/2 是基于 二进制帧(frames) 的多路复用协议。
- 一个连接上可以同时存在多个流(streams),每个流由唯一 ID 标识。
- 数据通过
DATA帧发送,支持服务器主动推送(Server Push) 和流式响应。 - 不再需要
Transfer-Encoding: chunked,因为帧本身就是分块的。
2.2 Go 实现(自动支持 HTTP/2)
Go 的 net/http 包在 TLS 启用时自动协商 HTTP/2(需使用 http.ListenAndServeTLS)。
package main
import (
"crypto/tls"
"fmt"
"log"
"net/http"
"time"
)
func streamHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(http.StatusOK)
flusher, ok := w.(http.Flusher)
if !ok {
http.Error(w, "Streaming unsupported", http.StatusInternalServerError)
return
}
for i := 1; i <= 5; i++ {
fmt.Fprintf(w, "HTTP/2 Chunk %d\n", i)
flusher.Flush()
time.Sleep(1 * time.Second)
}
}
func main() {
// 使用自签名证书(仅用于测试)
cert := "server.crt"
key := "server.key"
// 启动 HTTPS 服务(HTTP/2 需要 TLS)
server := &http.Server{
Addr: ":8443",
TLSConfig: &tls.Config{
NextProtos: []string{"h2"}, // 显式启用 HTTP/2
},
}
http.HandleFunc("/stream", streamHandler)
log.Println("HTTP/2 Server listening on https://localhost:8443")
log.Fatal(server.ListenAndServeTLS(cert, key))
}
🔐 生成自签名证书(测试用):
go run $(go env GOROOT)/src/crypto/tls/generate_cert.go --host localhost
✅ HTTP/2 下依然使用
Flusher,但底层传输是帧而非 chunked。
三、HTTP/3 的流式传输
3.1 原理
- HTTP/3 基于 QUIC 协议(UDP + TLS 1.3 + 多路复用)。
- 继承 HTTP/2 的语义(如流、头部压缩),但解决队头阻塞问题。
- 每个“流”在 QUIC 层是独立的,丢包不影响其他流。
- 同样支持流式响应,无需 chunked 编码。
3.2 Go 实现(需第三方库)
标准库 net/http 尚未原生支持 HTTP/3(截至 Go 1.23)。需使用社区库,如:
github.com/quic-go/quic-go- 其 HTTP/3 实现:
github.com/quic-go/quic-go/http3
示例代码(HTTP/3 流式服务)
package main
import (
"fmt"
"log"
"time"
"github.com/quic-go/quic-go/http3"
)
func streamHandler(w http3.ResponseWriter, r *http3.Request) {
w.Header().Set("Content-Type", "text/plain")
for i := 1; i <= 5; i++ {
fmt.Fprintf(w, "HTTP/3 Chunk %d\n", i)
w.Flush() // http3.ResponseWriter 内置 Flush
time.Sleep(1 * time.Second)
}
}
func main() {
// 使用 quic-go 提供的 HTTP/3 服务器
server := &http3.Server{
Addr: ":4433",
}
http3.HandleFunc("/stream", streamHandler)
log.Println("HTTP/3 Server listening on https://localhost:4433")
log.Fatal(server.ListenAndServeTLS("server.crt", "server.key"))
}
⚠️ 需要安装:
go get github.com/quic-go/quic-go
🌐 客户端需支持 HTTP/3(如 Chrome、curl >= 7.66 with
--http3)。
四、对比总结
| 特性 | HTTP/1.1 | HTTP/2 | HTTP/3 |
|---|---|---|---|
| 传输层 | TCP | TCP + TLS | UDP + QUIC |
| 多路复用 | ❌(需多个连接) | ✅(单连接多流) | ✅(QUIC 流) |
| 流式机制 | Chunked Encoding | DATA 帧 | QUIC 流 + DATA 帧 |
| 队头阻塞 | 存在 | 连接级无,但 TCP 有 | 无(QUIC 解决) |
| Go 支持 | 标准库原生 | 标准库(需 TLS) | 第三方库(quic-go) |
| 是否需 Flush | ✅ 必须 | ✅ 推荐 | ✅(部分自动) |
五、最佳实践建议
- 通用流式接口:无论协议版本,都应使用
http.Flusher(或对应接口)确保及时发送。 - 错误处理:流式过程中客户端可能断开,需检测
r.Context().Done()。 - 性能:避免频繁小块写入,适当缓冲(如每 1KB flush 一次)。
- 协议升级:优先部署 HTTPS 以启用 HTTP/2/3,提升并发性能。
六、扩展:SSE(Server-Sent Events)示例(基于 HTTP/1.1)
func sseHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
flusher := w.(http.Flusher)
i := 0
for {
select {
case <-r.Context().Done():
return
default:
fmt.Fprintf(w, "data: Message %d\n\n", i)
flusher.Flush()
i++
time.Sleep(2 * time.Second)
}
}
}
前端可通过 EventSource 接收:
const es = new EventSource('/sse');
es.onmessage = e => console.log(e.data);
以上由AI总结而来,AI写的还不错。。。
七、关于HTTP3的本地调试
http3在本地测试不太好搞,并不能像http、 http2的https那样,生成自签名证书,然后浏览器忽略警告就可以,还需做一些其他的设置。
首先证书得是系统信任的。
为了方面这里使用了一个工具:mkcert - https://github.com/FiloSottile/mkcert
使用方法:
mkcert -install # 安装CA
mkcert localhost 127.0.0.1 ::1 #生成本地测试的证书文件
mkcert -uninstall # 写在CA
用上面生成的证书文件来启动http3本地服务器。
浏览器直接访问还没用,不管是firefox还是chrome都不会用http3来连接,会提示连接不了。
然后得让浏览器强制使用http3协议来连接。
win系统下面的命令:
"C:\Program Files\Google\Chrome\Application\chrome.exe" --origin-to-force-quic-on=127.0.0.1:443 https://127.0.0.1:443
强制chrome使用http3连接才行,edge浏览器参数一样。firefox没找到相关启动参数,就没法了。
在142版的chrome和edge测试通过。
一些示例demo代码:https://github.com/ilaziness/gopkg/tree/main/net/http/stream
本文链接:https://360us.net/article/109.html