日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

fasthttp 快在哪里

發布時間:2024/4/11 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 fasthttp 快在哪里 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

坊間傳言 fasthttp 在某些場景下比 nginx 還要快,說明 fasthttp 中應該是做足了優化。我們來做一些相關的驗證工作。

先是簡單的 hello server 壓測。下面的結果是在 mac 上得到的,linux 下可能會有差異。

fasthttp:

wrk -c36 -t12 -d 5s http://127.0.0.1:8080 Running 5s test @ http://127.0.0.1:808012 threads and 36 connectionsThread Stats Avg Stdev Max +/- StdevLatency 267.58us 42.44us 0.90ms 79.18%Req/Sec 11.05k 391.97 11.79k 87.42%672745 requests in 5.10s, 89.18MB read Requests/sec: 131930.78 Transfer/sec: 17.49MB

標準庫:

wrk -c36 -t12 -d 5s http://127.0.0.1:8080 Running 5s test @ http://127.0.0.1:1000212 threads and 36 connectionsThread Stats Avg Stdev Max +/- StdevLatency 310.94us 163.45us 14.41ms 93.42%Req/Sec 9.74k 1.01k 12.80k 75.82%593327 requests in 5.10s, 63.37MB read Requests/sec: 116348.87 Transfer/sec: 12.43MB

rust 的 actix,編譯選項帶 --release:

wrk -c36 -t12 -d 5s http://127.0.0.1:9999 Running 5s test @ http://127.0.0.1:999912 threads and 36 connectionsThread Stats Avg Stdev Max +/- StdevLatency 267.31us 20.52us 622.00us 86.07%Req/Sec 11.11k 364.03 11.64k 82.68%676329 requests in 5.10s, 68.37MB read Requests/sec: 132629.68 Transfer/sec: 13.41MB

好家伙,雖然是 hello 服務,但是 fasthttp 在性能上竟然趕上 rust 編寫的服務了,確實有點夸張。這也間接證明了,“某些場景”至少可能真的和不帶 GC 的語言性能差不多。

說明 fasthttp 里所做的優化是值得我們做點研究的。不過 fasthttp 搞的這些優化點也并不是很神奇,首先是很常見的 goroutine workerpool,對創建的 goroutine 進行了重用,其本身的 workerpool 結構:

type workerPool struct {// Function for serving server connections.// It must leave c unclosed.WorkerFunc ServeHandler.....ready []*workerChan }

核心就是 ready 數組,該數組的元素是已經創建出來的 goroutine 的 job channel。

type workerChan struct {lastUseTime time.Timech chan net.Conn }

這么多年過去了,基本的 workerpool 的模型還是沒什么變化。最早大概是在 handling 1 million requests with go 提到,fasthttp 中的也只是稍有區別。

具體的請求處理流程也比較簡單:

tcp accept -> workerpool.Serve -> 從 workerpool 的 ready 數組中獲取一個 channel -> 將當前已 accept 的連接發送到 channel 中 -> 對端消費者調用 workerFunc

這里這個 workerFunc 其實就是 serveConn,之所以不寫死成 serveConn 主要還是為了在測試的時候能替換掉做 mock,不新鮮。

主要的 serve 流程:

func (s *Server) serveConn(c net.Conn) (err error) {for {ctx := s.acquireCtx(c)br = acquireReader(ctx) // or br, err = acquireByteReader(&ctx)// read request header && bodybw = acquireWriter(ctx)s.Handler(ctx) // 這里就是 listenAndServe 傳入的那個 handlerif br != nil {releaseReader(s, br)}if bw != nil {releaseWriter(s, bw)}if ctx != nil {s.releaseCtx(ctx)}} }

在整個 serve 流程中,幾乎所有對象全部都進行了重用,ctx(其中有 Request 和 Response 結構),reader,writer,body read buffer。可見作者對于內存重用達到了偏執的程度。

同時,對于 header 的處理,rawHeaders 是個大 byte 數組。解析后的 header 的 value 如果是字符串類型,其實都是指向這個大 byte 數組的,不會重復生成很多小對象:

如果是我們自己寫這種 kv 結構的 header,大概率就直接 map[string][]string 上了。

通過閱讀 serveConn 的流程我們也可以發現比較明顯的問題,在執行完用戶的 Handler 之后,fasthttp 會將所有相關的對象全部釋放并重新推進對象池中,在某些場景下,這樣做顯然是不合適的,舉個例子:

當用戶流程中異步啟動了 goroutine,并且在 goroutine 中使用 ctx.Request 之類對象時就會遇到并發問題,因為在 fasthttp 的主流程中認為 ctx 的生命周期已經結束,將該 ctx 放回了 sync.Pool,然而用戶依然在使用。想要避免這種問題,用戶需要將各種 fasthttp 返回的對象人肉拷貝一遍。

從這點上來看,基于 sync.Pool 的性能優化往往也是有代價的,無論在什么場景下使用 sync.Pool,都需要對應用程序中的對象生命周期進行一定的假設,這種假設并不見得適用于 100% 的場景,否則這些手段早就進標準庫,而非開源庫了。

對于庫的用戶來說,這樣的優化手段輕則帶來更高的心智負擔,重則是線上 bug。在使用開源庫之前,還是要多多注意。非性能敏感的業務場景,還是用標準庫比較踏實。

參考資料

[1] https://github.com/valyala/fasthttp

[2] https://medium.com/smsjunk/handling-1-million-requests-per-minute-with-golang-f70ac505fca

總結

以上是生活随笔為你收集整理的fasthttp 快在哪里的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。