天天看點

淘系架構團隊:golang vs java

1、直接基于 epoll 的簡單 http: 沒有完整的 http decoder 和 route (如 gnet, ulib 直接簡單的位元組拼接,固定的路由 handler 回調)

2、zero copy 和記憶體複用: 内部處理位元組的 0 拷貝(go 官方 http 庫為了減少開發者的出錯機率,沒有使用 zero copy,否則開發者可能在無意中引用了已經放回 buff 池内的的資料造成沒有意識到的并發問題等等),而記憶體複用,大部分架構或多或少都已經做了。

3、prefork:注意到 go 架構中有使用了 prefork 程序的方式(比如 fasthttp-prefork),這是 fork 出多個子程序,共享同一個 listen fd,且每個程序使用單核但并發(1 個 P)處理的邏輯可以避免 go runtime 内部的鎖競争和 goroutine 排程的消耗(但是 go runtime 中為了并發和 goroutine 排程而存在的相關“無用”代碼的消耗還是會有一些)

4、語言本身的性能差異對于第一點,其實簡化了各種編解碼和路由之後,雖然提高了性能,但是往往會降低架構的易用性,對于一般的業務而言,不會出現如此高的 QPS,同時選擇架構的時候往往還需要考慮易用性和可擴充性等,同時還需要考慮到公司内部原有中間件或者 SDK 所使用的架構內建複雜度。

對于第二點,如果是作為一個網絡代理而言,沒有業務方的開發,往往可以使用真正的完全 zero copy,但是作為業務開發架構提供出去的話是需要考慮一定的業務出錯機率,往往犧牲一部分性能是劃算的。

第三點 prefork , java netty 等是直接對于線程操作,可以更加定制化的優化性能,而 go 的 goroutine 需要的是一個通用協程,目的是降低編寫并發程式的難度,在這個層次上難免性能比不上一個優化的非常出色的 Java 基于線程操作的架構;但是直接操作線程的話需要合理控制好線程數,這是個比較頭疼的調優問題(特别是對于新手來說),而 goroutine 則可以不關心池子的大小,使得代碼更加優雅和簡潔,這對于工程品質保障其實是一個提升。另外這裡存在 prefork 是由于 go 沒法直接操作線程,而 fasthttp 提供了 prefork 的能力,使用多程序方式來對标 Java 的多線程來進一步提高性能。

第四點,語言本身來說 Java 還是更加的成熟,包括 JVM 的 Jit 能力也使得在熱代碼中和 Go 編譯型語言的差異不大,何況 Go 本身的編譯器還不是特别成熟,比如逃逸分析等方面的問題, Go 本身的記憶體模型和 GC 的成熟度也比不上 Java。還有很重要的一點,Go 的架構成熟度和 Java 也不在一個級别,但相信這些都會随着時間逐漸成熟。

總之,對于這個架構壓測資料意義在于了解性能天花闆,判斷繼續優化的空間和 ROI (投入産出比)。具體選擇架構還是要根據使用場景,性能,易用性,可擴充性,穩定性以及公司内部的生态等作出選擇,語言和性能分别隻是其中一個因素。

各種架構的應用場景不同導緻其優化側重點不同,如 spring web 為了易用性,可擴充性和穩定性而犧牲了性能,但它同樣擁有龐大的社群和使用者。再比如 Service Mesh Sidecar 場景下 Go 的天然并發程式設計上的優勢,以及小記憶體占用,快速啟動,編譯型語言等特點使得比 Java 更加适合。

(附:其實我使用上述代碼和 dockerfile 建構,并且使用同樣的壓測腳本,在阿裡雲 4 核獨享機器測試下 go fasthttp-easyjson-prefork 架構 Json serialization 的性能要高于 Java wizzardo-http 和 firenio-http-lite 30% 以上且延遲更低的,這可能和核心有關)。

以上為淘系架構團隊風弈的個人見解,歡迎前來讨論。