Go標(biāo)準(zhǔn)庫http與fasthttp服務(wù)端性能對(duì)比場景分析_第1頁
Go標(biāo)準(zhǔn)庫http與fasthttp服務(wù)端性能對(duì)比場景分析_第2頁
Go標(biāo)準(zhǔn)庫http與fasthttp服務(wù)端性能對(duì)比場景分析_第3頁
Go標(biāo)準(zhǔn)庫http與fasthttp服務(wù)端性能對(duì)比場景分析_第4頁
Go標(biāo)準(zhǔn)庫http與fasthttp服務(wù)端性能對(duì)比場景分析_第5頁
已閱讀5頁,還剩9頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

第Go標(biāo)準(zhǔn)庫http與fasthttp服務(wù)端性能對(duì)比場景分析目錄1.背景2.性能測試3.對(duì)結(jié)果的簡要分析4.優(yōu)化途徑

1.背景

Go初學(xué)者學(xué)習(xí)Go時(shí),在編寫了經(jīng)典的hello,world程序之后,可能會(huì)迫不及待的體驗(yàn)一下Go強(qiáng)大的標(biāo)準(zhǔn)庫,比如:用幾行代碼寫一個(gè)像下面示例這樣擁有完整功能的webserver:

//來自/pkg/net/http/#example_ListenAndServe

packagemain

import(

"io"

"log"

"net/http"

funcmain(){

helloHandler:=func(whttp.ResponseWriter,req*http.Request){

io.WriteString(w,"Hello,world!\n")

http.HandleFunc("/hello",helloHandler)

log.Fatal(http.ListenAndServe(":8080",nil))

}

gonet/http包是一個(gè)比較均衡的通用實(shí)現(xiàn),能滿足大多數(shù)gopher90%以上場景的需要,并且具有如下優(yōu)點(diǎn):

標(biāo)準(zhǔn)庫包,無需引入任何第三方依賴;對(duì)http規(guī)范的滿足度較好;無需做任何優(yōu)化,即可獲得相對(duì)較高的性能;支持HTTP代理;支持HTTPS;無縫支持HTTP/2。

不過也正是因?yàn)閔ttp包的均衡通用實(shí)現(xiàn),在一些對(duì)性能要求嚴(yán)格的領(lǐng)域,net/http的性能可能無法勝任,也沒有太多的調(diào)優(yōu)空間。這時(shí)我們會(huì)將眼光轉(zhuǎn)移到其他第三方的http服務(wù)端框架實(shí)現(xiàn)上。

而在第三方http服務(wù)端框架中,一個(gè)行如其名的框架fasthttp被提及和采納的較多,fasthttp官網(wǎng)宣稱其性能是net/http的十倍(基于gotestbenchmark的測試結(jié)果)。

fasthttp采用了許多性能優(yōu)化上的最佳實(shí)踐,尤其是在內(nèi)存對(duì)象的重用上,大量使用sync.Pool以降低對(duì)GoGC的壓力。

那么在真實(shí)環(huán)境中,到底fasthttp能比net/http快多少呢?恰好手里有兩臺(tái)性能還不錯(cuò)的服務(wù)器可用,在本文中我們就在這個(gè)真實(shí)環(huán)境下看看他們的實(shí)際性能。

2.性能測試

我們分別用net/http和fasthttp實(shí)現(xiàn)兩個(gè)幾乎零業(yè)務(wù)的被測程序:

nethttp:

///bigwhite/experiments/blob/master/http-benchmark/nethttp/main.go

packagemain

import(

_"expvar"

"log"

"net/http"

_"net/http/pprof"

"runtime"

"time"

funcmain(){

gofunc(){

for{

log.Println("當(dāng)前routine數(shù)量:",runtime.NumGoroutine())

time.Sleep(time.Second)

http.Handle("/",http.HandlerFunc(func(whttp.ResponseWriter,r*http.Request){

w.Write([]byte("Hello,Go!"))

log.Fatal(http.ListenAndServe(":8080",nil))

}

fasthttp:

///bigwhite/experiments/blob/master/http-benchmark/fasthttp/main.go

packagemain

import(

"fmt"

"log"

"net/http"

"runtime"

"time"

_"expvar"

_"net/http/pprof"

"/valyala/fasthttp"

typeHelloGoHandlerstruct{

funcfastHTTPHandler(ctx*fasthttp.RequestCtx){

fmt.Fprintln(ctx,"Hello,Go!")

funcmain(){

gofunc(){

http.ListenAndServe(":6060",nil)

gofunc(){

for{

log.Println("當(dāng)前routine數(shù)量:",runtime.NumGoroutine())

time.Sleep(time.Second)

s:=fasthttp.Server{

Handler:fastHTTPHandler,

s.ListenAndServe(":8081")

}

對(duì)被測目標(biāo)實(shí)施壓力測試的客戶端,我們基于hey這個(gè)http壓測工具進(jìn)行,為了方便調(diào)整壓力水平,我們將hey包裹在下面這個(gè)shell腳本中(僅適于在linux上運(yùn)行):

///bigwhite/experiments/blob/master/http-benchmark/client/http_client_load.sh

#./http_client_load.sh31000010GET81:8080

echo"$0task_numcount_per_heyconn_per_heymethodurl"

task_num=$1

count_per_hey=$2

conn_per_hey=$3

method=$4

url=$5

start=$(date+%s%N)

for((i=1;i=$task_num;i++));do{

tm=$(date+%T.%N)

echo"$tm:task$istart"

hey-n$count_per_hey-c$conn_per_hey-m$method$urlhey_$i.log

tm=$(date+%T.%N)

echo"$tm:task$idone"

}done

end=$(date+%s%N)

count=$(($task_num*$count_per_hey))

runtime_ns=$(($end-$start))

runtime=`echo"scale=2;$runtime_ns/1000000000"|bc`

echo"runtime:"$runtime

speed=`echo"scale=2;$count/$runtime"|bc`

echo"speed:"$speed

該腳本的執(zhí)行示例如下:

bashhttp_client_load.sh81000000200GET34:8080

http_client_load.shtask_numcount_per_heyconn_per_heymethodurl

16:58:09.146948690:task1start

16:58:09.147235080:task2start

16:58:09.147290430:task3start

16:58:09.147740230:task4start

16:58:09.147896010:task5start

16:58:09.148314900:task6start

16:58:09.148446030:task7start

16:58:09.148930840:task8start

16:58:45.001080740:task3done

16:58:45.241903500:task8done

16:58:45.261501940:task1done

16:58:50.032383770:task4done

16:58:50.985076450:task7done

16:58:51.269099430:task5done

16:58:52.008164010:task6done

16:58:52.166402430:task2done

runtime:43.02

speed:185960.01

從傳入的參數(shù)來看,該腳本并行啟動(dòng)了8個(gè)task(一個(gè)task啟動(dòng)一個(gè)hey),每個(gè)task向34:8080建立200個(gè)并發(fā)連接,并發(fā)送100whttpGET請(qǐng)求。

我們使用兩臺(tái)服務(wù)器分別放置被測目標(biāo)程序和壓力工具腳本:

目標(biāo)程序所在服務(wù)器:81(物理機(jī),Intelx86-64CPU,40核,128G內(nèi)存,CentOs7.6)

$cat/etc/redhat-release

CentOSLinuxrelease7.6.1810(Core)

$lscpu

Architecture:x86_64

CPUop-mode(s):32-bit,64-bit

ByteOrder:LittleEndian

CPU(s):40

On-lineCPU(s)list:0-39

Thread(s)percore:2

Core(s)persocket:10

NUMA節(jié)點(diǎn):2

廠商ID:GenuineIntel

CPU系列:6

型號(hào):85

型號(hào)名稱:Intel(R)Xeon(R)Silver4114CPU@2.20GHz

步進(jìn):4

CPUMHz:800.000

CPUmaxMHz:2201.0000

CPUminMHz:800.0000

BogoMIPS:4400.00

虛擬化:VT-x

L1d緩存:32K

L1i緩存:32K

L2緩存:1024K

L3緩存:14080K

NUMA節(jié)點(diǎn)0CPU:0-9,20-29

NUMA節(jié)點(diǎn)1CPU:10-19,30-39

Flags:fpuvmedepsetscmsrpaemcecx8apicsepmtrrpgemcacmovpatpse36clflushdtsacpimmxfxsrssesse2sshttmpbesyscallnxpdpe1gbrdtscplmconstant_tscartarch_perfmonpebsbtsrep_goodnoplxtopologynonstop_tscaperfmperfeagerfpupnipclmulqdqdtes64ds_cplvmxsmxesttm2ssse3sdbgfmacx16xtprpdcmpciddcasse4_1sse4_2x2apicmovbepopcnttsc_deadline_timeraesxsaveavxf16crdrandlahf_lmabm3dnowprefetchepbcat_l3cdp_l3intel_ptssbdmbaibrsibpbstibptpr_shadowvnmiflexpriorityeptvpidfsgsbasetsc_adjustbmi1hleavx2smepbmi2ermsinvpcidrtmcqmmpxrdt_aavx512favx512dqrdseedadxsmapclflushoptclwbavx512cdavx512bwavx512vlxsaveoptxsavecxgetbv1cqm_llccqm_occup_llccqm_mbm_totalcqm_mbm_localdthermidaaratplnptspkuospkespec_ctrlintel_stibpflush_l1d

壓力工具所在服務(wù)器:33(物理機(jī),鯤鵬arm64cpu,96核,80G內(nèi)存,CentOs7.9)

#cat/etc/redhat-release

CentOSLinuxrelease7.9.2009(AltArch)

#lscpu

Architecture:aarch64

ByteOrder:LittleEndian

CPU(s):96

On-lineCPU(s)list:0-95

Thread(s)percore:1

Core(s)persocket:48

NUMA節(jié)點(diǎn):4

型號(hào):0

CPUmaxMHz:2600.0000

CPUminMHz:200.0000

BogoMIPS:200.00

L1d緩存:64K

L1i緩存:64K

L2緩存:512K

L3緩存:49152K

NUMA節(jié)點(diǎn)0CPU:0-23

NUMA節(jié)點(diǎn)1CPU:24-47

NUMA節(jié)點(diǎn)2CPU:48-71

NUMA節(jié)點(diǎn)3CPU:72-95

Flags:fpasimdevtstrmaespmullsha1sha2crc32atomicsfphpasimdhpcpuidasimdrdmjscvtfcmadcpopasimddpasimdfhm

我用dstat監(jiān)控被測目標(biāo)所在主機(jī)資源占用情況(dstat-tcdngym),尤其是cpu負(fù)荷;通過[expvarmon監(jiān)控memstats],由于沒有業(yè)務(wù),內(nèi)存占用很少;通過gotoolpprof查看目標(biāo)程序中對(duì)各類資源消耗情況的排名。

下面是多次測試后制作的一個(gè)數(shù)據(jù)表格:

圖:測試數(shù)據(jù)

3.對(duì)結(jié)果的簡要分析

受特定場景、測試工具及腳本精確性以及壓力測試環(huán)境的影響,上面的測試結(jié)果有一定局限,但卻真實(shí)反映了被測目標(biāo)的性能趨勢。我們看到在給予同樣壓力的情況下,fasthttp并沒有10倍于nethttp的性能,甚至在這樣一個(gè)特定的場景下,兩倍于net/http的性能都沒有達(dá)到:我們看到在目標(biāo)主機(jī)cpu資源消耗接近70%的幾個(gè)用例中,fasthttp的性能僅比net/http高出30%~70%左右。

那么為什么fasthttp的性能未及預(yù)期呢?要回答這個(gè)問題,那就要看看net/http和fasthttp各自的實(shí)現(xiàn)原理了!我們先來看看net/http的工作原理示意圖:

圖:nethttp工作原理示意圖

http包作為server端的原理很簡單,那就是accept到一個(gè)連接(conn)之后,將這個(gè)conn甩給一個(gè)workergoroutine去處理,后者一直存在,直到該conn的生命周期結(jié)束:即連接關(guān)閉。

下面是fasthttp的工作原理示意圖:

圖:fasthttp工作原理示意圖

而fasthttp設(shè)計(jì)了一套機(jī)制,目的是盡量復(fù)用goroutine,而不是每次都創(chuàng)建新的goroutine。fasthttp的Serveraccept一個(gè)conn之后,會(huì)嘗試從workerpool中的ready切片中取出一個(gè)channel,該channel與某個(gè)workergoroutine一一對(duì)應(yīng)。一旦取出channel,就會(huì)將accept到的conn寫到該channel里,而channel另一端的workergoroutine就會(huì)處理該conn上的數(shù)據(jù)讀寫。當(dāng)處理完該conn后,該workergoroutine不會(huì)退出,而是會(huì)將自己對(duì)應(yīng)的那個(gè)channel重新放回workerpool中的ready切片中,等待這下一次被取出。

fasthttp的goroutine復(fù)用策略初衷很好,但在這里的測試場景下效果不明顯,從測試結(jié)果便可看得出來,在相同的客戶端并發(fā)和壓力下,net/http使用的goroutine數(shù)量與fasthttp相差無幾。這是由測試模型導(dǎo)致的:在我們這個(gè)測試中,每個(gè)task中的hey都會(huì)向被測目標(biāo)發(fā)起固定數(shù)量的[長連接(keep-alive)],然后在每條連接上發(fā)起飽和請(qǐng)求。這樣fasthttpworkerpool中的goroutine一旦接收到某個(gè)conn就只能在該conn上的通訊結(jié)束后才能重新放回,而該conn直到測試結(jié)束才會(huì)close,因此這樣的場景相當(dāng)于讓fasthttp退化成了net/http的模型,也染上了net/http的缺陷:goroutine的數(shù)量一旦多起來,goruntime自身調(diào)度所帶來的消耗便不可忽視甚至超過了業(yè)務(wù)處理所消耗的資源占比。下面分別是fasthttp在200長連接、8000長連接以及16000長連接下的cpuprofile的結(jié)果:

200長連接:

(pprof)top-cum

Showingnodesaccountingfor88.17s,55.35%of159.30stotal

Dropped150nodes(cum=0.80s)

Showingtop10nodesoutof60

flatflat%sum%cumcum%

0.46s0.29%0.29%101.46s63.69%/valyala/fasthttp.(*Server).serveConn

00%0.29%101.46s63.69%/valyala/fasthttp.(*workerPool).getCh.func1

00%0.29%101.46s63.69%/valyala/fasthttp.(*workerPool).workerFunc

0.04s0.025%0.31%89.46s56.16%internal/poll.ignoringEINTRIO(inline)

87.38s54.85%55.17%89.27s56.04%syscall.Syscall

0.12s0.075%55.24%60.39s37.91%bufio.(*Writer).Flush

00%55.24%60.22s37.80%net.(*conn).Write

0.08s0.05%55.29%60.21s37.80%net.(*netFD).Write

0.09s0.056%55.35%60.12s37.74%internal/poll.(*FD).Write

00%55.35%59.86s37.58%syscall.Write(inline)

(pprof)

8000長連接:

(pprof)top-cum

Showingnodesaccountingfor108.51s,54.46%of199.23stotal

Dropped204nodes(cum=1s)

Showingtop10nodesoutof66

flatflat%sum%cumcum%

00%0%119.11s59.79%/valyala/fasthttp.(*workerPool).getCh.func1

00%0%119.11s59.79%/valyala/fasthttp.(*workerPool).workerFunc

0.69s0.35%0.35%119.05s59.76%/valyala/fasthttp.(*Server).serveConn

0.04s0.02%0.37%104.22s52.31%internal/poll.ignoringEINTRIO(inline)

101.58s50.99%51.35%103.95s52.18%syscall.Syscall

0.10s0.05%51.40%79.95s40.13%runtime.mcall

0.06s0.03%51.43%79.85s40.08%runtime.park_m

0.23s0.12%51.55%79.30s39.80%runtime.schedule

5.67s2.85%54.39%77.47s38.88%runtime.findrunnable

0.14s0.07%54.46%68.96s34.61%bufio.(*Writer).Flush

16000長連接:

(pprof)top-cum

Showingnodesaccountingfor239.60s,87.07%of275.17stotal

Dropped190nodes(cum=1.38s)

Showingtop10nodesoutof46

flatflat%sum%cumcum%

0.04s0.015%0.015%153.38s55.74%runtime.mcall

0.01s0.0036%0.018%153.34s55.73%runtime.park_m

0.12s0.044%0.062%153s55.60%runtime.schedule

0.66s0.24%0.3%152.66s55.48%runtime.findrunnable

0.15s0.055%0.36%127.53s46.35%poll

127.04s46.17%46.52%127.04s46.17%runtime.epollwait

00%46.52%121s43.97%/valyala/fasthttp.(*workerPool).getCh.func1

00%46.52%121s43.97%/valyala/fasthttp.(*workerPool).worker

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論