HTTP 框架 Hertz 实践入门:性能测试指南

2021 年 9 月 8 日,字节跳动宣布正式开源 CloudWeGo 。CloudWeGo 是一套字节跳动内部微服务中间件集合,具备高性能、强扩展性和稳定性的特点,专注于解决微服务通信与治理的难题,满足不同业务在不同场景的诉求 。2022 年 6 月 21 日,Hertz 正式开源 。Hertz 链接:https://GitHub.com/cloudwego/hertz,欢迎大家共同参与建设^_^
 
日前,CloudWeGo 团队正式开源字节跳动最大的 HTTP 框架 Hertz 。Hertz 在发布之后得到了大量用户的关注,开源四个月以来,Hertz 已经收获了 2k+ star 。有很多用户自己进行了测试,感谢社区对我们的关注和支持 。
本文旨在分享开发者在压测 Hertz 时需要了解的场景和技术问题 。这些建议有助于用户更好地结合真实 HTTP 场景对 Hertz 进行调优,使之更贴合业务需要、发挥最佳性能 。用户也可以参考官方提供的压测项目 hertz-benchmark [1]了解更多细节 。
1. 微服务 HTTP 场景的特点
Hertz 诞生于字节跳动大规模微服务架构实践,面向的场景自然是微服务场景,因此下面会先介绍微服务 HTTP 场景的特点,方便开发者深入理解 Hertz 在其中的设计思考 。
 
  • HTTP 通信模型
 
微服务间的通信通常以 Ping-Pong 模型为主,除了常规的吞吐性能指标外,每次 HTTP 的平均时延也是开发者需要考虑的点 。吞吐达到瓶颈时可以通过增加机器快速解决,但对用户使用体验有显著影响的时延却没有那么容易降低 。在微服务场景下,一次调用往往需要多个微服务协作完成,即使每个节点延迟很低,最终汇聚到链路上的时延也会被放大,因此微服务场景下时延指标是开发者更应该关注的点 。Hertz 在保证吞吐的前提下,也针对时延做了一定优化 。
 
  • 长短连接使用
 
由于 TCP 连接首次建立时需要三次握手,如果每个请求都建立新连接,这部分的开销是非常大的 。因此对于时延敏感型服务,尽量使用长连接完成请求 。在 HTTP 1.1 中,长连接也是默认的选项 。但是没有银弹,维持连接也需要消耗资源,长连接的水平扩展能力也不如短连接 。因此,在某些场景下并不适合使用长连接,比如定时拉取配置的场景,在这个场景下,建连时延对配置影响并不大,且当配置中心负载过高时,希望能够方便的进行水平扩容,这时短连接可能是一个更好的选择 。
 
  • 包体积大小
 
一个服务的包大小取决于实际的业务场景 。HTTP 场景的数据可以放在 query、path、header、body 等地方,不同位置对解析造成的影响也不一样 。HTTP 的 header 是标识符协议,在没有找到特定的标识符之前,框架并不知道 header 还有多少,因此框架需要收到全部的 header 后才能够解析完成,对框架的内存模型不很友好 。Hertz 也针对 header 解析做了特殊的优化,分配足够的 buffer 空间给 header,减少 header 处理时跨包拷贝的开销 。
同时在字节跳动内部线上服务的统计中,发现大部分包在 1K 以内(但是太小的包没有实际意义,比如固定返回 "hello world"),同时大包场景上不封顶,各个包大小均有涉及,所以 Hertz 在最常用的 128k 以内的包的性能(吞吐和时延)进行了重点优化 。
 
  • 并发数量
 
每个实例的上游可能会有很多个,不会只接受某个实例的请求;而且,HTTP 1 的连接不能够多路复用,每条连接上只能同时处理一个请求 。因此 server 需要接受多个连接同时处理 。不同服务的连接使用率也不同,比如压测服务的连接使用率很高,一个请求完成后马上就会进行下一个请求;有的服务连接使用率很低,虽然是长连接,但是只使用一次 。这两者使用的连接模型并不相同,前者应使用 goroutine per connection 的模型减少上下文的切换,后者应使用协程池减少过多 goroutine 的调度开销 。Hertz 也同时支持这两种场景,用户可以根据自己的业务场景选择合适的配置 。
2. 针对 HTTP 场景进行压测
2.1 使用贴近自己的场景
Github 上的压测项目有很多,网络上也有很多性能测试报告,但是这些项目和测试不一定贴合自己 。举个极端一点的例子,在真实场景中你会写一个项目无论 client 发什么 server 都只回 hello world 吗?很遗憾,很多的压测项目就是这么做的 。
在进行压测前,应考虑自己真正的使用场景,比如: