HTTP框架Hertz實踐入門:效能測試指南

陶然陶然發表於2022-11-22

  2021 年 9 月 8 日,位元組跳動宣佈正式開源 CloudWeGo。CloudWeGo 是一套位元組跳動內部微服務中介軟體集合,具備高效能、強擴充套件性和穩定性的特點,專注於解決微服務通訊與治理的難題,滿足不同業務在不同場景的訴求。2022 年 6 月 21 日,Hertz 正式開源。

  日前,CloudWeGo 團隊正式開源位元組跳動 HTTP 框架 Hertz。Hertz 在釋出之後得到了大量使用者的關注,開源四個月以來,Hertz 已經收穫了 2k+ star。有很多使用者自己進行了測試,感謝社群對我們的關注和支援。

  本文旨在分享開發者在壓測 Hertz 時需要了解的場景和技術問題。這些建議有助於使用者更好地結合真實 HTTP 場景對 Hertz 進行調優,使之更貼合業務需要、發揮較高效能。使用者也可以參考官方提供的壓測專案 hertz-benchmark 瞭解更多細節。

   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 嗎?很遺憾,很多的壓測專案就是這麼做的。

  在進行壓測前,應考慮自己真正的使用場景,比如:

  長短連線的使用:使用長連線還是短連線更符合自己的場景。

  連線使用率的估算:如果使用長連線,且連線使用率很高(大部分場景),則使用預設配置即可;如果連線使用率很低,可以新增配置:server.WithIdleTimeout(0),將 goroutine per connection 的模型修改為協程池模型,並進行對比測試。

  資料位置及大小的確定:上面提到不同位置(如 query、header、body 等)及大小的資料對框架可能造成影響,如果所有框架的效能都比較一般,可以考慮換一個資料傳輸位置。

  併發數的確定:有的服務屬於輕業務重框架,這個時候框架的併發可能會很高;有的服務屬於重業務輕框架,這個時候框架的併發可能會很低。

  如果只是想看一下框架的效能,可以使用常規的場景:長連線、較高連線使用率、1k body、100 併發等。hertz-benchmark 倉庫預設的壓測配置也是如此。同時 hertz-benchmark 倉庫也開發給使用者 header、body、併發數的配置,使用者可以方便的修改這些配置完成貼合自己的壓測。

  2.1.1 確定壓測物件

  衡量一個 HTTP 框架的效能需要從兩個視角分別去思考:Client 視角與 Server 視角。在大規模的業務架構中,上游 Client 不見得使用的也是下游的框架,而開發者呼叫的下游服務也同樣如此,如果再考慮到 Service Mesh 的情況就更復雜了。

  一些壓測專案通常會把 Client 和 Server 程式混部進行壓測,然後得出整個框架的效能資料,這其實和線上實際執行情況很可能是不符的。

  如果要壓測 Server,應該給 Client 儘可能多的資源,把 Server 壓到極限,反之亦然。如果 Client 和 Server 都只給了 4 核 CPU 進行壓測,會導致開發者無法判斷最終得出來的效能資料是哪個視角下的,更無法給線上服務做實際的參考。

  2.1.2 使用獨佔 CPU

  雖然線上應用通常是多個程式共享 CPU,但在壓測場景下,Client 與 Server 程式都處於極端繁忙的狀況,此時共享 CPU 會導致大量上下文切換,從而使得資料缺乏可參考性,且容易產生前後很大波動。

  所以我們建議是將 Client 與 Server 程式隔離在不同 CPU 或者不同獨佔機器上進行。如果還想要進一步避免其他程式產生影響,可以再加上 nice -n -20 命令調高壓測程式的排程優先順序。

  另外如果條件允許,相比雲平臺虛擬機器,使用真實物理機會使得測試結果更加嚴謹與具備可復現性。

   3. 效能資料參考

  在滿足上述要求的前提下,我們基於當前最新版本對多個框架進行了壓測對比,壓測程式碼在 hertz-benchmark 倉庫。在充分壓滿 Server 的目標下,Hertz 的 P99 延遲在所有壓測框架中最低,吞吐也是屬於第一梯隊,且在持續最佳化中。

  CPU: AMD EPYC 7Y83 64-Core Processor 2.7GHz

  執行限定 server 4-CPUs,client 16-CPUs

  OS:Debian GNU/Linux 10 (buster)

  Go 1.19

  hertz v0.3.2,fasthttp v1.40.0,gin v1.8.1,fiber v2.38.1  

四個框架的吞吐和時延比較  

三個框架的吞吐和時延比較

   4. 結語

  Hertz 作為一個超大規模企業級的微服務 HTTP 框架,其在設計之初就更傾向於解決大規模微服務場景下的各種問題。在推廣過程中也遇到了各種各樣的服務,踩了各種各樣的坑,也是基於這些服務和遇到的問題寫了本文。歡迎廣大開發者基於本文提供的測試指南,針對自己的實際場景選擇合適的工具。

來自 “ 位元組跳動技術團隊 ”, 原文作者:尹旭然;原文連結:http://server.it168.com/a2022/1122/6776/000006776430.shtml,如有侵權,請聯絡管理員刪除。

相關文章