流式传输与大请求
模块在 h2 流与 nginx 请求之间做的是流式搬运,不是「先收完再处理」。这一页讲清四条路径的 边界与要调的旋钮。
响应:下行流式 + 背压
| 项 | 行为 |
|---|---|
| 高水位 | CFD_RESP_HWM = 256 KiB:待写给边缘的字节超过它就暂停读源站 |
| 恢复 | 边缘把窗口放开、outbound 排空后继续读 |
| 大响应 | 定长、分块(chunked)、静态文件三条路径都逐字节对账通过(2 MiB / 1 MiB 规模) |
| 写失败 | 先排空出缓冲再造新字节(nginx 没设 SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER,OpenSSL 要求 WANT_WRITE 后同指针同长度重试) |
请求:上行流式
content-length已知:首个 DATA 到达就注入 nginx,后续分片入队并唤醒读侧,不等传完。- 长度未知:走
Transfer-Encoding: chunked流式注入(0.5.0 起没有「缓冲上限 413」这条路)。 - 现在没有
CFD_MAX_BUFFERED_BODY这种整体缓冲上限 —— 限制来自背压预算,见下。
接收侧背压五层
严格对齐 cloudflared 的「按真正交给源站的字节记账」,并按预算暂停 / 淘汰 / 拒新流:
| 层 | 判据 | 默认 | 指令 |
|---|---|---|---|
| 第 1 层 | 按「真正交给 nginx 的字节」记全局账 | cfd_tunnel_stream_bq_hwm = 1m | 单流高水位 |
| 第 2 层 | 按预算暂停 | cfd_tunnel_global_bq_limit = 64m | 全局预算 |
| 第 3 层 | 停滞淘汰(整窗口零接收才算停滞) | cfd_tunnel_stream_stall_window = 60000 ms、stall_min_bq = 512k、stall_ratio = 0(关闭) | 停滞 |
| 第 4 层 | 预算淘汰 | — | 预算 |
| 第 5 层 | 拒新流 | cfd_tunnel_global_bq_reject_ratio = 90(%) | 拒绝线 |
连接级窗口早还默认 on:这是唯一一处有意偏离 cloudflared 的背压行为(off 一键退回严格 对齐)。取舍见 与 cloudflared 的差异。
内存上界(多隧道时按隧道数相乘):
text
请求体缓冲上界 ≈ N × cfd_tunnel_global_bq_limit # N = 隧道条数,默认 64m3 条隧道就是把上界顶到 192 MiB —— 要压住就调小 cfd_tunnel_global_bq_limit。
gzip
| 场景 | 行为 |
|---|---|
源站 Content-Encoding: gzip | 透明处理(模块用 zlib,CFD_GZ_OUT_CAP 64 KiB / 块 16 KiB) |
源站被 gzip on 动态压缩 | 同上,走 event pipe 的 /gzbuf 路径也覆盖过 |
| 源站发了坏 gzip | 按错误流处理,不把坏字节当正常响应 |
trailer 与头
- 请求 trailer:合成后交给 nginx(
$http_trailer可见);单 name 上限CFD_REQ_NAME_MAX= 64 B, trailer 名最多CFD_REQ_MAX_HDRS= 192 条。 - 响应 trailer:从 nginx 侧收到后写回 h2 流。
什么时候会看到 502 / 503
| 状态 | 触发 |
|---|---|
502 | 远程规则里命中了本模块没有能力的 service(unix: / hello_world / 未知 scheme)且本地没写 inject_listen;或远程配置体/外层 JSON 解不出来 |
503 | 开了 remote_config 但没有任何可用规则、本地也没写 inject_listen(cloudflared 的「没有 ingress 规则」兜底) |
413 | client_max_body_size 超限(nginx 自己的语义) |
RST_STREAM(REFUSED_STREAM) | 裸 TCP 隧道流(不是 HTTP 请求,没有状态码) |