找回密码
 立即注册
首页 业界区 业界 [DotNet] Kestrel 框架中, http1 与 http2 的性能对比 ...

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

炳裘垦 昨天 16:20
作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢!

  • cnblogs博客
  • zhihu
  • Github
  • 公众号:一本正经的瞎扯
    1.png

(文中的 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# 服务端


  • 源码位置: https://github.com/ahfuzhang/QiWa/tree/v0.1-http-compare/code-snippets/Http2EchoServer
  • 使用 dotnet 10 编译

    • make -f Makefile_linux build

  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的模式压测


  • 源码请看: https://github.com/ahfuzhang/QiWa/tree/v0.1-http-compare/code-snippets/GolangHttp2Client
  • 编译: make build
  • 运行: make run
golang 客户端 + 每个 tcp 连接上多个并发的模式


  • 源码: https://github.com/ahfuzhang/QiWa/tree/v0.1-http-compare/code-snippets/GolangHttp2ClientV2
  • 编译: make build
  • 运行: make run
  • 最佳配置:

    • tcp 连接数 8
    • 每个 tcp 连接上 40 个并发

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


  • 源码: https://github.com/ahfuzhang/QiWa/tree/v0.1-http-compare/code-snippets/GolangHttp2ClientV3
  • 编译: make build
  • 运行: make run
  • 最佳配置:

    • tcp 连接数 16
    • 每个 tcp 连接上 80 个并发stream (不是 80 个协程)

总结


  • http2 虽然比 http1 快了 4 倍,但是 rpc 的场景,自定义协议肯定更快
  • c 实现的 nghttp2 的性能很强悍,我用 golang 实现的版本与之还有一大段距离
  • Kestrel 框架的性能相当强悍 (虽然多核情况下可能会变慢)

希望对你有用。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

6 小时前

举报

您需要登录后才可以回帖 登录 | 立即注册