为什么在代理服务器上测试, http2 的转发性能比 http 1 更低? 📅 发布时间:2026/7/15 8:31:25 👁️ 浏览次数: 作者:张富春(ahfuzhang)转载时请注明作者和引用链接谢谢cnblogs博客zhihuGithub公众号:一本正经的瞎扯我在测试 Http2 server 与 Http 1.1 server 的性能差异时最高测试数据是 http2 比 http1 快 3.6 倍。而 Carter 在测试 apisix 代理服务器的性能的时候得到的数据是 http2 的性能只有 http 1 的 80%。我们曾彼此质疑对方的数据并觉得不可思议我的观点是二进制协议一定快过文本协议没理由在代理服务器上测试的数据会导致 http2 慢于 http 1。今天终于想明白了原因其实我们都没错结论是在代理服务器上http2 的转发性能会低于 http 1。导致这一项差异的关键是splice()系统调用也就是代理服务器中实现零拷贝的关键。(后续大量引用 ChatGPT 的回答)splice() 是什么splice() 是 Linux 的一个系统调用C 里通过 splice(2) 暴露用来在 两个文件描述符之间搬运数据并且尽量走 零拷贝zero-copy 路径减少用户态缓冲区参与。最典型的用途文件 ↔ 管道、管道 ↔ socket用它可以把数据从磁盘文件直接“送”到网络连接里中间不需要 read() 到用户态再 write() 回内核。⸻它解决什么问题传统写法/* by 01022.hk - online tools website : 01022.hk/zh/qrcode.html */ read(file_fd, user_buf, n); write(sock_fd, user_buf, n);缺点数据会在内核 ↔ 用户态之间拷贝还可能污染 CPU cache、占用内存带宽。splice() 的目标让数据尽量在内核内部移动比如从 page cache / pipe buffer 直接到 socket buffer减少一次或多次拷贝。⸻它的基本形态语义/* by 01022.hk - online tools website : 01022.hk/zh/qrcode.html */ ssize_t splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags);• fd_in / fd_out输入/输出 fd • off_in / off_out • 传 NULL 表示使用 fd 当前偏移并推进像普通读写那样 • 非 NULL 则用你提供的偏移类似 pread/pwrite 的味道并且不一定修改 fd 的文件偏移具体取决于对象类型 • len最多搬运多少字节 • flags控制阻塞/移动行为例如 SPLICE_F_NONBLOCK 等返回值实际搬运的字节数0 通常表示 EOF-1 表示出错errno 说明原因。⸻常见组合file → pipe → socket高性能“发送文件”• splice(file_fd → pipe_write_end)• splice(pipe_read_end → sock_fd)这类组合常用于实现“类似 sendfile 的效果”但 splice 更通用尤其适合你还要在中间做点别的内核态处理的场景。pipe ↔ pipe用于在管道之间转移数据比如配合 tee() 做复制。⸻适用限制很重要• 并不是任意 fd 都支持。splice 需要底层文件操作实现支持VFS/驱动/协议栈。• 在 Linux 上pipe 经常是 splice 的“中转站”很多场景要求 fd_in 或 fd_out 至少有一端是 pipe或者最终在内核内部用 pipe buffer 做承载。• 数据来源如果是普通文件通常来自 page cache因此能做到少拷贝或零拷贝。⸻和 sendfile() 的关系• sendfile()更“专用”通常是 文件 → socket 的快速路径。• splice()更“通用”可以在更多类型的 fd 之间搬运常见用法是 通过 pipe 作为桥 来拼接成 file→socket 的零拷贝链路。就算 splice 把内核中 socket buffer 的数据转移到了 pipe 下面用户使用时也仍然要 read()似乎并没有做到零拷贝对你理解得很准确只要最终要在用户态“看见/处理”字节流就绕不开一次 copy_to_user。splice(socket → pipe) 只是把“从 socket 读到用户缓冲区”这一步换成了“从 socket 读到内核 pipe”因此它不能消除“内核 → 用户态”的拷贝你后面 read(pipefd, userbuf, …) 仍然会拷贝。更精确地说• 传统路径用户态消费recv()/read(socket, userbuf)→ 内核 socket 接收队列 → 用户态 userbuf一次 copy_to_user• splice 路径用户态仍要消费splice(socket → pipe)内核内搬运/重挂页避免 copy_to_user然后 read(pipe → userbuf)→ pipe → 用户态 userbuf还是一次 copy_to_user所以“用户态要拿到 payload”这一目标下splice 不会减少总拷贝次数到 0。它可能减少的是• 少一次“多余的中间 buffer 拷贝/搬运”比如你原来先 read 到用户再 write 到另一个 fd• 或减少 CPU 开销某些场景下从 socket 直接挂到 pipe、再 splice 到目标 fd什么时候 splice 真正有意义能把用户态拷贝降到 0纯转发不解析数据socket_in → pipe → socket_out / file你不在用户态触碰数据只做转发/落盘。典型反向代理、L7 但不解包、录流到文件等。“只看少量头部 大量 body” 的混合比如 HTTP你在用户态只 recv 一小段 header 做决策body 用 splice 走零拷贝转发。这类能显著减少 copy_to_user 的总量因为大头 body 不进用户态。⸻你真正关心的点TCP 通讯里的拷贝到底能不能减少• 应用层必须处理 payload解码、压缩、JSON、protobuf…至少一次内核→用户拷贝不可避免把字节交给进程。• 应用层不处理 payload只转发可以做到 0 次进入用户态splice/送 NIC 相关 offload 另说。对于代理服务器 是不是可以理解为 http 1 大部分都情况能做内核态的四层转发而 http 2 只能做用户态的七层转发?不完全对但你这个直觉抓到了一半决定能不能“像 L4 那样内核态转发”的关键不是 HTTP/1 vs HTTP/2而是代理要不要理解协议语义、要不要“终止连接并重建连接”。 HTTP/2 因为多路复用 流控 头压缩确实更容易把你“逼到”用户态 L7但 HTTP/1 也不是天然就能内核态转发。把它拆成几种代理形态你会更清楚真·L4 代理TCP/四层负载均衡• 不看 HTTP只转发字节流客户端 TCP ↔ 后端 TCP。• 这对 HTTP/1 和 HTTP/2 都一样都能做“内核友好”的转发例如 splice/零拷贝路径、甚至内核里的 TPROXY/IPVS 等。• 代价你做不了按 URL/Host/Header 的路由、鉴权、限速等 L7 能力。✅ 结论HTTP/2 也可以做 L4 转发只是此时你根本不知道它是 HTTP/2。HTTP/1 的 L7 反代常见 Nginx/Envoy 的那种• 你要读请求行/headers决定路由可能改 header可能缓存、压缩、重试等。• 这就已经是 用户态 L7 了不是“内核态 L4”。不过 HTTP/1 有一个“容易优化”的点• header/body 分界清晰且通常一个连接同一时刻服务一个请求即便 keep-alive也多是串行管线化很少用。• 所以可以做 “只在用户态读少量 header然后 body 用 splice 直通”大 body 上传/下载时很香。✅ 结论HTTP/1 不是“大部分都能内核态 L4 转发”它只是 更容易做“半 L7 半零拷贝直通”。HTTP/2 的 L7 反代stream 级路由、多路复用• 同一条 TCP 连接里交错多个 stream 的 frames。• 你要按 stream 拆帧、调度、维护流控窗口、处理 HPACK。• 前后两端通常是两条独立连接客户端一条 H2后端另一条 H2 或 H1stream id/窗口/HPACK 动态表都不可能原封不动“直通”。这使得 把大段 payload 当作连续字节“splice 过去”非常困难因为你必须先在用户态把属于某个 stream 的 DATA 帧摘出来再发到后端而且还可能要重排/限速/优先级。✅ 结论HTTP/2 做 L7 反代时基本只能用户态为主或者说必须有“协议终止点”在用户态/实现层。⸻更准确的一句话• HTTP/1更容易把“body”做成内核态直通splice/sendfile用户态只处理少量控制面headers。• HTTP/2只要你要做 L7按 stream/headers 路由就很难做到 body 完全内核态直通因为 framing/mux/flow-control 迫使你参与数据分发。• 但 HTTP/2 依然可以做纯 L4 转发只是你失去所有 L7 能力。总结http 1.1 的协议特点header 和 body 有明显的区分body 的长度非常明确一条 tcp 连接上同时只有一个请求响应 —— 因此在代理服务器的场景 http 1 很容易使用 splice 这样零拷贝机制来优化。特别是 body 部分这样更长的内容直接在内核态就能够实现数据交换完全不需要把数据拷贝到用户态再从用户态发送出去。http 2 在一条 tcp 连接上有多个流每个流超出SETTINGS_MAX_FRAME_SIZE后还会分成多个 frame —— 由此决定了 http2 上通过 splice 实现零拷贝优化很困难或者说能够真正零拷贝的场景变少了。对于服务器而言http 2 这样的二进制协议确实比 http 1 更快特别是像 api server / rcp server 这样的数据包小请求频繁的场景http2 比 http1 快 3.6 倍就不奇怪了。如果是图片下载/文件下载/大吞吐量的数据传输选择 http1 更好
Nodejs+vue+ElementUI诊所药品仓库管理系统 文章目录技术栈概述系统核心功能模块后端实现(Node.js)前端实现(Vue ElementUI)关键技术与优化部署与运维扩展方向--nodejs技术栈--结论源码文档获取/同行可拿货,招校园代理 :文章底部获取博主联系方式!技… 2026/7/12 18:02:15
[嵌入式系统-210]:模拟电路和数字电路的全方位的比较 模拟电路(Analog Circuit)与数字电路(Digital Circuit)是电子系统的两大基石,它们在信号表示、设计方法、噪声敏感性、应用场景等方面存在根本性差异。以下从12个维度进行全方位、系统性对比,并揭示其融合趋… 2026/7/14 5:57:34
推进科研和工程,编程跻身顶级人类竞赛榜:谷歌Gemini 3 Deep Think重大升级 谷歌刚刚发布了Gemini 3 Deep Think重大更新。剑指科研和工程!宇宙级难度编程算法竞赛Codeforces获得了3455分,跻身全球第8,只有7个人类超越它。Gemini 3 Deep Think模拟人类科学家的深度思考模式,将解决复杂科学与工程难题的能力… 2026/7/13 14:22:27
网盘直链解析技术实战:突破限速的浏览器脚本解决方案 网盘直链解析技术实战:突破限速的浏览器脚本解决方案 【免费下载链接】baiduyun 油猴脚本 - 一个免费开源的网盘下载助手 项目地址: https://gitcode.com/gh_mirrors/ba/baiduyun 在云存储服务普及的今天,网盘直链下载助手已成为技术用户突破下载… 2026/7/15 8:30:24
Hacker Podcast前端架构深度剖析:Next.js + Tailwind CSS + shadcn/ui的完美组合 Hacker Podcast前端架构深度剖析:Next.js Tailwind CSS shadcn/ui的完美组合 Hacker Podcast是一个基于AI的Hacker News中文播客项目,每天自动抓取Hacker News热门文章,通过AI生成中文总结并转换为播客内容。该项目采用了Next.js、Tailwin… 2026/7/15 8:28:24
STM32F103 HAL库+FATFS文件系统KEIL工程:SD卡读写、目录管理、带中文注释与烧录指南 本文还有配套的精品资源,点击获取 简介:一套开箱即用的STM32F103 FATFS文件系统实现方案,基于ST官方HAL库构建,适配KEIL MDK-ARM开发环境。工程已集成SD卡(SPI/SDIO模式)或SPI Flash的底层驱动ÿ… 2026/7/15 8:28:24
KLara未来路线图:卡巴斯基GReAT团队揭秘下一代恶意软件狩猎功能 KLara未来路线图:卡巴斯基GReAT团队揭秘下一代恶意软件狩猎功能 【免费下载链接】klara Kasperskys GReAT KLara 项目地址: https://gitcode.com/gh_mirrors/kl/klara 在网络安全威胁日益复杂的今天,恶意软件狩猎工具已成为安全研究人员的必备利器… 2026/7/15 8:26:24
如何使用Apache Pulsar Go Client高效构建消息生产者?核心API与最佳实践 如何使用Apache Pulsar Go Client高效构建消息生产者?核心API与最佳实践 【免费下载链接】pulsar-client-go Apache Pulsar Go Client Library 项目地址: https://gitcode.com/gh_mirrors/pu/pulsar-client-go Apache Pulsar Go Client是连接Apache Pulsar消… 2026/7/15 8:24:23
5分钟快速上手OpenSpeedy:免费游戏加速工具的完整指南 5分钟快速上手OpenSpeedy:免费游戏加速工具的完整指南 【免费下载链接】OpenSpeedy 🎮 An open-source game speed modifier. 项目地址: https://gitcode.com/gh_mirrors/op/OpenSpeedy 你是否曾在游戏中遇到帧率限制,希望获得更流畅的… 2026/7/15 8:24:23
行星减速机的工作原理是什么?从齿轮运动关系到减速比计算 一、行星齿轮机构的组成 标准行星齿轮机构主要包括: 太阳轮; 行星轮; 内齿圈; 行星架。 太阳轮位于机构中心。 多个行星轮围绕太阳轮均匀布置,行星轮内侧与太阳轮外啮合,外侧与内齿圈内啮合。 行星轮通过轴… 2026/7/15 0:03:00
阅读Java开源框架源码的心得分享! 前几日闲来无事有幸看到了一位博主分享自己阅读开源框架源码的心得,看了之后也引发了我的一些深度思考。我们为什么要看源码?我们该怎么样去看源码? 其中前者那位博主描述的我觉得很全了(如下图所示),就不做… 2026/7/15 0:03:00
【LINUX】驱动 【LINUX驱动】【字符设备】【中断】【Platform】【网课 设备树】【GPIO】【PINCTRL】【INPUT】【IIC】【SPI】【网络驱动】【屏幕驱动】【一 设备树】【二 内核模块编译】【三 基本驱动框架】【四 Platform总线设备驱动框架】【五 驱动子系统】【六 综合】 2026/7/15 0:07:01
Git reset 与 revert 深度对比:5个关键差异与 3 种典型应用场景 Git Reset 与 Revert 深度对比:5个关键差异与3种典型应用场景在团队协作开发中,代码版本管理如同行走钢丝——一步失误可能导致整个项目陷入混乱。作为Git进阶用户,你是否曾在深夜面对错误的提交束手无策?是否在强制推送后收到同事… 2026/7/13 8:31:55
GitHub 学生包申请避坑:5个常见失败原因与开发者工具调试方案 GitHub 学生包申请技术排障指南:5个高频失败场景与开发者工具实战方案第一次尝试申请GitHub学生包时,我盯着屏幕上那个不断转圈的加载动画整整15分钟,最终只等来了一行冰冷的错误提示。这可能是许多开发者共同的经历——明明按照教程操作&… 2026/7/14 18:25:04
冒烟测试用例设计规范:5%-10%覆盖率下的3类核心场景与执行标准 冒烟测试用例设计的黄金法则:5%-10%覆盖率下的精准筛选策略在快节奏的敏捷开发环境中,冒烟测试作为质量保障的第一道防线,其重要性不言而喻。当测试资源有限而时间紧迫时,如何从海量测试用例中精准筛选出那关键的5%-10%࿰… 2026/7/14 5:09:41