[DotNet] Kestrel 框架中, http1 与 http2 的性能对比

[复制链接]
发表于 2026-2-26 14:58:17 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢!
(文中的 http2 是指明文的 http2 协议,也叫 h2c, 并未测试 TLS 加密的环境)
假如仅从协议的角度对比,http2 会比 http1 更快吗?假如更快,能快多少?
基于以上疑问,我基于 C# 的 Kestrel 框架,做了一个协议性能的对比。
结论

节省各人的时间,先说结论:

  • 在同样的运行环境,同样的业务逻辑环境下。最好环境,http2 比 http1 快 4.03 倍

    • 从处理处罚字节数上看:http1 均匀每个哀求 230 字节,http2 均匀每个哀求 112 字节。就单个哀求而言,http2 的包体积只有 http1 的包的 48.7%.

  • http2 是二进制协议,理论上肯定比 http1 如许的文本协议更快。

    • 发挥 http2 的性能上风的关键参数是 MaxStreamsPerConnection (我测试时设置为 200),也就是说,肯定要在一个 tcp 上并发多个 stream,才华发挥 http2 的性能上风。

  • 从应用上说:

    • api 服务、rpc 服务,使用 http2 更好
    • 文件下载(图片、资源文件等)、html 页面输出、文件上传、大数据量的 post 等等,使用 http1 更好

      • 使用 http1 在署理服务器上也能得到性能上风。请看前一篇: 为什么在署理服务器上测试, http2 的转发性能比 http 1 更低?


  • 是否简单的使用 http2 的客户端,就能轻松实现 http2 比 http1 提拔了 4 倍?答案是否定的,http2 的客户端并不简单。

    • 我一共使用了四种 http2 的客户端来测试:
    • 使用 nghttp2 客户端:C 语言实现,专门用于压测的工具,测试得到 http2 比 http1 快 4.03 倍
    • 使用 golang 客户端,每个 HttpClient 对象对应一个协程,每个协程内一发一收:http2 的吞吐量是  http1 的 80%

      • 假如以 http1 的模式来使用 http2,http2 会比 http1 慢

    • 使用 golang 客户端,每个 HttpClient 对象上限定只有一个 tcp 毗连,每个 HttpClient 对应 40 个协程一发一收:http2 的吞吐量是  http1 的 1.86 倍

      • 通过限定 tcp 毗连,来让每个 tcp 毗连上并行多个 stream,这才是 http2 client 的精确用法
      • 压测客户端启动两个进程:http2 的吞吐量是  http1 的 2.28 倍,由此阐明 golang 的 http2 的 client 内部有许多锁,单个进程不如多个进程性能好。

    • 使用 golang 客户端,完全基于 tcp 协议来实现,每个 tcp 毗连上,一个协程专门用于 send,一个协程专门用于 recv: http2 的吞吐量是  http1 的 2.68 倍

      • nghttp2 客户端大概做了一些 socket option 的优化,导致做到了最好的压测性能
      • 假如渴望做到 http2 上的极致性能,基于 tcp 来实现是个好主意


压测环境阐明

基于 kestrel 的C# 服务端

  1. $(DOTNET) publish $(PRJ).csproj \
  2.           -r linux-x64 \
  3.           -p:DefineConstants=UNIX -p:AllowUnsafeBlocks=true \
  4.           -p:PublishAot=true \
  5.           -p:StripSymbols=false \
  6.           --self-contained true \
  7.           -c Release -o $(BUILD_DIR)
复制代码

  • http1 和 http2 采取同样的 callback 函数
服务器运行环境


  • 运行于 linux amd64 环境,在 docker 容器中运行
  • 限定一个 cpu

    • CPU 型号:Intel(R) Core(TM) Ultra 7 265KF, 小核, 4.5GHz

  • 内存 256 MB
  • 限定线程池的线程数为 1: ThreadPool.SetMaxThreads(1, 1)

    • 为何限定,请看前一篇:C#的 ThreadPool.SetMaxThreads() 设置最大线程数到底对性能有多大影响

  • 客户端在同一个呆板上哀求服务器,只管到达单核 100% 的 CPU 占用率,然后在客户端统计 QPS
  1. docker run -it --rm \
  2.           --platform=linux/amd64 \
  3.           --cpuset-cpus="19" \
  4.                 -m 256m \
  5.           -v $(BUILD_DIR):/app \
  6.           --network=host \
  7.           mcr.microsoft.com/dotnet/runtime:10.0 \
  8.           /app/Http2EchoServer \
  9.             -http2.port=9081 \
  10.                 -http1.port=9082 \
  11.                 -threadpool.max=1
复制代码
nghttp2 压测


  • 压测 http1 端口的下令如下:
  1. docker run --rm -it --network host goodideal/nghttp2:latest \
  2.                 h2load -p http/1.1 -c 120 -t 8 -n 2000000 \
  3.                 http://127.0.0.1:9082/echo?seq=9999
复制代码

  • 120 个 tcp 毗连时,测试得到最优性能表现
  • 压测 http2 的下令如下:
  1. docker run --rm -it --network host goodideal/nghttp2:latest \
  2.                 h2load -p h2c -c 8 -t 8 --max-concurrent-streams 60 -n 1000000 \
  3.                 http://127.0.0.1:9081/echo?seq=8888
复制代码

  • 最优性能组合为:

    • 毗连数 8
    • max-concurrent-streams = 60, 每个 tcp 毗连上并发 60 个 stream

golang 客户端+http1的模式压测

golang 客户端 + 每个 tcp 毗连上多个并发的模式

golang 客户端 + 基于 tcp 协议来实现 http2 客户端

总结


  • http2 固然比 http1 快了 4 倍,但是 rpc 的场景,自界说协议肯定更快
  • c 实现的 nghttp2 的性能很霸道,我用 golang 实现的版本与之尚有一大段间隔
  • Kestrel 框架的性能相称霸道 (固然多核环境下大概会变慢)
渴望对你有用。😃
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表